显式复制构造函数

24

我扩展了std::string以满足我编写自定义函数的需求,这些函数内置于字符串类中,称为CustomString

我定义了构造函数:

    class CustomString : public std::string {
    public:
        explicit CustomString(void);
        explicit CustomString(const std::string& str);
        explicit CustomString(const CustomString& customString);
        //assignment operator
        CustomString& operator=(const CustomString& customString);
    ... };

在第三个构造函数(复制构造函数)和赋值运算符中,其定义为:

CustomString::CustomString(const CustomString& customString):
    std::string(static_cast<std::string>(customString)) 
{}
CustomString& CustomString::operator=(const CustomString& customString){
    this->assign(static_cast<std::string>(customString));
    return *this;
}

由于这是一个“显式”转换,也就是需要使用显式类型转换才能将其分配给另一个CustomString对象;因此它正在抱怨该赋值语句。

CustomString s = CustomString("test");

我不确定在哪里需要明确地进行类型转换。

如果复制构造函数不是显式的,代码可以正常工作,但我想知道并实现显式定义,而不是“猜测适当的转换”。

3个回答

50

显示复制构造函数意味着复制构造函数不会被隐式调用,而这正是在表达式中发生的事情:

CustomString s = CustomString("test");
这个表达式的字面意思是:使用接受一个const char*参数的构造函数创建一个临时的CustomString。隐式地调用CustomString的拷贝构造函数,将该临时对象复制到s中。
如果代码是正确的(即拷贝构造函数不是显式的),编译器将避免创建临时对象并省略复制操作,直接使用字符串字面值构造s。但编译器仍需检查构造过程是否可行,在这里可能会失败。
你可以显式调用拷贝构造函数:
CustomString s( CustomString("test") );

但我建议您完全避免使用临时变量,只需使用const char*创建s

CustomString s( "test" );

这正是编译器本来要做的事情...


1
哦,我完全理解你的答案(它是正确的答案,我已经标记为最佳答案),但是我该如何实现例如CustomString s = customStringObjectOnStack; - abumusamq
@mkhan3189,只有当复制构造函数不是显式的时,你才能实现CustomString s = customStringObjectOnStack。对于第二个问题,kDelimited是CustomString还是其他什么东西?此外,由于+函数是const的,所以c.append(static_cast<const std::string&>(*this))将会起作用。 - ForEveR
@ForEveR 没错,谢谢你。我已经自己找到答案了。kDelimiter是CustomString常量对象,没错。c.append(static_cast<std::string>(*this))可以用于隐式复制构造函数声明。所以一切都好。这个线程可以关闭了。 - abumusamq
5
你可以通过显式调用复制构造函数 CustomString s( customStringObjectOnStack ); 来实现 CustomString s = customStringObjectOnStack;,而不是隐式地使用 = 语法来调用复制构造函数。 - David Rodríguez - dribeas

7
从std :: string继承不安全,因为std :: string没有虚析构函数。至于您的问题-您的复制构造函数不应该是显式的,以允许以下用法:
CustomString s = "test";

我不知道为什么您想将复制构造函数声明为explicit,因为这是不必要的。 只有在将CustomString对象声明为以下内容时,才会使用显式的复制构造函数:

CustomString s(CustomString("test"));

@ildjarn: 为什么需要懒惰模式?这样做是正确的(虽然不是最优的),但可以解决问题并提供解决方案... - David Rodríguez - dribeas
@DavidRodríguez-dribeas 当 ildjarn 发表评论时,回答相对较短。 - ForEveR
谢谢你的回答,很好。但这样做有点懒,因为我提出这个问题是为了改进我的编程方式,而不仅仅是完成任务。无论如何,还是谢谢你 :) - abumusamq
3
只要原帖子只使用 CustomString 并且不尝试通过 std::string 指针删除 new CustomString,这个派生类真的存在任何不安全的问题吗? - Joshua Green
只要[...], 是否存在任何不安全的事情?除非您有可靠且持续执行条件的方法,包括同事的代码和未来可能复制粘贴到的任何地方,否则对于该问题的回答始终是肯定的。无论[...]中是什么。 - spectras

0

目前的gcc似乎不会调用在gcc 8.2(MinGW.org GCC-8.2.0-5)中测试的复制构造函数,并且将直接调用构造函数来创建X1。

#include <iostream>

using std::cout;
using std::endl;


class X1 {
public:

    X1() {
        cout<< "invoke constructor" << endl;
    }; 

    explicit X1(const X1 &obj)  { 
        cout << "invoke copy constructor" << endl;
    };

    X1(X1&& obj)  {
        cout << "invoke move constructor" << endl;
    };

    X1& operator=(const X1 &obj) {
        cout << "invoke value assign constructor " << endl;
        if(this == &obj) {
            return *this;
        }
        return *this;
    };

    X1& operator=(const X1 && obj) {
        cout << "invoke move value assign constructor " << endl;
        if(this == &obj) {
            return *this;
        }
        return *this;

    };

    ~X1() {
        cout << "invoke deconstruct" << endl;
    };



};

int main() {
//    X1 l1(1);
//    X1 l3(2);
    X1 l4 = X1();

    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接