复制构造函数的诡异行为,哪位C++高手可以解释一下?
C++ Primer(4 edition) 中文版P407上说:“复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。”但我的实验结果显示是没有调用复制构造函数。”
我做了个实验(实验1),首先写了一个简单的类,里面提供一个默认构造函数,一个接受单个形参的构造函数,一个复制构造函数和一个=重载函数,在上面每个函数中都打印一个消息,提示这个函数正在被调用。
接着在主函数使用这个类,使用复制初始化这个类的对象。按照书上说的,显示结果应该是先调用单形参的构造函数创建临时对象,输出一个提示,然后调用复制构造函数,输出一个提示。但结果只有一个提示!显示的是只调用了单形参的构造函数直接创建了对象,并没有临时对象的存在。结果很诡异!
既然没有调用复制构造函数,我又做了个实验(实验2),把这个复制构造函数改成private的,其他地方没有变,做了同样的实验,编译就不通过了,错误提示是我的主函数调用了复制构造函数。结果是不是很诡异?
问题:C++在复制初始化的时候到底有没有创建临时对象?如果有,为什么会出现实验1的结果;如果没有为什么出现实验2的结果?
实验1的类定义代码如下:
- C/C++ code
class Employee{public: Employee(); Employee(std::string sname); // copy constructor Employee(const Employee &old); Employee& operator=(const Employee &old); int getID() const { return id; } std::string getName() const { return name; }private: static int newid; int id; std::string name;};实验2的类定义代码如下:
- C/C++ code
class Employee{public: Employee(); Employee(std::string sname); Employee& operator=(const Employee &old); int getID() const { return id; } std::string getName() const { return name; }private: // copy constructor Employee(const Employee &old); static int newid; int id; std::string name;};实验的类实现代码如下:
- C/C++ code
int Employee::newid = 1;Employee::Employee() : id(newid){ std::cout << "In default constructor, id = " << id << std::endl; ++newid;}Employee::Employee(std::string sname) : id(newid), name(sname){ std::cout << "In single parameter constructor, id = " << id << std::endl; ++newid;}// copy constructorEmployee::Employee(const Employee &old) : id(newid), name(old.getName()){ std::cout << "In copy constructor, id = " << id << std::endl; ++newid;}Employee& Employee::operator=(const Employee &old){ std::cout << "In aissign function, id = " << id << std::endl; name = old.getName(); return *this;}主函数使用类的代码如下:
- C/C++ code
int main(){ string name = "adu"; Employee em5 = name; return 0;}实验1结果:
In single parameter constructor, id = 1
实验2结果:
error: ‘Employee::Employee(const Employee&)’ is private
[解决办法]
因为优化的结果
[解决办法]
Employee(std::string sname); 从参数类型向类类型转换。用explicit禁止。
Employee e; // 默认构造函数
Employee e1 = e; // 复制初始化
Employee e2; // 默认构造函数
e2 = e; // 赋值初始化
实验2 VS2010正常运行。
[解决办法]
[解决办法]
不诡异,编译器可以优化掉不必要的临时变量。
------解决方案--------------------
你的构造函数:
Employee();
Employee(std::string sname);
Employee(const Employee &old);
你的拷贝函数:
Employee& operator=(const Employee &old);
实验1:实例化对象通过 但是执行拷贝函数时 类型不匹配 必须为Employee类型
解决方法:
1、只用构造函数而不用拷贝函数
- C/C++ code
Employee em5 = name;改成Employee em5(name); //用到了Employee(std::string sname)这个构造函数
[解决办法]
这句话,印象中primer确实有
string str="ihello"; 就说这句代码
首先编译器: 讲ihello这个字符串创建了一个 临时的 string对象,然后
用这个临时对象 进行赋值拷贝了了str
只不过编译器优化了,看不出来
说的不对,请指教
[解决办法]
很多理论上需要先执行"构造函数"创建一个临时对象,再执行"复制构造函数"的地方,都会被编译器优化,省去"复制构造函数"的步骤,也就是省去临时对象。
比如下面的函数,理论上也是应该在函数内部先创建一个临时局部对象,再用"复制构造函数"构造a,但实际上只有一次普通构造函数。
Object f()
{
...
return Object();
}
Object a = f();
[解决办法]
[解决办法]
我觉得这是编译器优化的结果
C++ Primer 4中文版407页还说过:“当情况许可时,可以允许编译器跳过复制构造函数直接创建对象,但编译器没有义务这样做。”
可见编译器跳过复制构造函数直接对象的情况是存在的。
[解决办法]
优化过了
[解决办法]