赞
踩
所有代码win11的vs2017 版本15.9.24 中测试。
书中原文如下:
12.1.2 特殊成员函数
C++自动提供了下面这些成员函数:
• 默认构造函数,如果没有定义构造函数:
• 默认析构函数,如果没有定义;
• 复制构造函数,如果没有定义;
• 赋值运算符,如果没有定义:
• 地址运算符,如果没有定义。
下面为了方便举例,我们假设有这样一个类:
class A {
int a;
public:
int geta(void) { return a; }
...
};
如果没有提供任何构造函数,C++将创建默认构造函数,即编译器将提供一个不接受任何参数 ,也不执行任何操作的构造函数。函数形如:
A::A() { } // 通过默认构造函数创建的对象的值是未知的
验证代码:
#include<iostream>
using namespace std;
class A {
int a;
public:
int geta(void) { return a; }
};
#if 1
int main(void) {
A a;
cout << a.geta() << endl; // 输出结果不一定
}
#endif
对于没有提供任何构造函数的理解是,你不能定义一个A::A(int n){a = n;}的构造函数,然后还期望存在一个默认构造函数。
更具体的说就是,如果你定义了A::A(int n){a = n;}的构造函数,那么你只能这样创建类A的对象,而不赋初值是非法的。
验证代码:
#include<iostream> using namespace std; class A { int a; public: int geta(void) { return a; } A(int t) { a = t; } }; #if 1 int main(void) { A a(3); cout << a.geta() << endl; A b; // 此处编译报错,提示如下: //错误 C2512 “A”: 没有合适的默认构造函数可用 //错误(活动) E0291 类 "A" 不存在默认构造函数 cout << b.geta() << endl; } #endif
它用于初始化过程中(包括按值传递参数)而不是常规赋值过程中!复制构造函数原型通常如下:
Class_name(const Class_name &);
新建上个对象并将其初始化为同类现有对象时。下面4种声明都将调用复制构造函数
StringBad ditto(motto); // calls StringBad(const StringBad &)
StringBad metoo = motto; // calls StringBad (const StringBad &)
StringBad also = StringBad(motto); // calls StringBad(const StringBad &)
StringBad * pStringBad = new StringBad(motto); // calls StringBad (const StringBad &)
前3种形式多少为了初始化一个对象(而不是一个对象指针),但是由于第2,3中方式中有等号,所以可能会生成一个临时变量,然后调用赋值运算符来赋值。
所以初始化一个对象强烈建议使用方式一,即变量后面用括号赋值初始化的值的方式,这样能避免调用赋值运算符来赋值,能保证执行更快是一方面,另一方面是确保不会执行赋值运算符来赋值。
至于如何验证是仅使用复制构造函数还是也会使用赋值运算符来赋值,那就需要自定义复制构造函数和赋值运算符,在里面加打印来确认了。
先说结论,上述的方式全部都只会使用复制构造函数,不会使用赋值运算符来赋值。当然,这个结果是符合预期的,毕竟明明是没必要去多执行一次赋值运算符来赋值的。但是不排除别的环境,别的VS版本,甚至的别的优化等级下表现不同。所以建议自行测试。
验证代码:
#include<iostream> using namespace std; class A { int a; public: int geta(void) { return a; } A(int t) { a = t; } A(const A& t) { a = t.a; cout << "use copy \n"; } A & operator=(const A& t) { a = t.a; cout << "use operator\n"; return *this; } }; #if 1 int main(void) { A a(1004); cout << "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n init way: A aa(a)\n"; A aa(a); cout << aa.geta() << "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; cout << "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n init way: A b=a\n"; A b=a; cout << b.geta() << "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; cout << "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n init way: A c = A(a);\n"; A c = A(a); cout << c.geta() << "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; cout << "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n init way: A* p = new A(a);\n"; A* p = new A(a); cout << p->geta() << "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; cout << "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n operator= test: b=a\n"; b = a; cout << b.geta() << "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; } #endif
输出结果:
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv init way: A aa(a) use copy 1004 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv init way: A b=a use copy 1004 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv init way: A c = A(a); use copy 1004 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv init way: A* p = new A(a); use copy 1004 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv operator= test: b=a use operator 1004 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ANSI C允许结构赋值,而C++允许类对象赋值,这是通过自动为类重载赋值运算符实现的。
这种运算符的原型如下:
Class_name & Class_name: :operator=(const Class_name &) ;
它接受并返回一个指向类对象的引用。例如,StringBad类的赋值运算符的原型如下:
StringBad & StringBad :: operator=(const StringBad &);
与默认复制构造函数的功能类似,标黄的的差异点。默认赋值运算符的功能是:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。