《深度探索C++对象模型》第二章:构造函数语义学

类型转换运算符函数:

operator 类型名() { //由类型名来确定返回值类型
    实现转换的语句
    ...
}

while(cin >> val) 原理
cin是一个流对象,而>>运算符返回左边的流对象,也就是说cin >> val返回cin,于是while(cin >> val)就变成了while(cin),问题就变成了一个流对象在判断语句中的合法性。

operator void *() const;函数在while(cin)或是if(cin)时被调用,将流对象转换成void*类型。
bool operator!() const;函数在while(!cin)或是if(!cin)时被调用,将流对象转换成bool类型。

#include<iostream>  
using namespace std;  
     
class A  
{  
    public:  
        A() {}  
        ~A() {}  
        operator void* () const
        {  
            cout << "cast to void*; ";  
            return (void *)this;  
        }  
        bool operator ! () const
        {  
            cout << "cast to bool; ";  
            return true;  
        }  
};  
     
int main()  
{  
    A a;  
    if (a) cout << "first" << endl;  
    if (!a) cout << "second" << endl;  
    return 0;  
}
//cast to void*; first
//cast to bool; second。

Conversion 运算符实际上很难在一种可预期的良好行为模式下使用。它的引入是明智的,但是测试是严酷的。

由此引出了implicit和explicit关键词

2.1 Default Constructor 的构造操作

default constructors 在需要的时候被编译器产生出来

以下四种情况,会造成“编译器必须为未声明constructors的classes合成一个default constructor”:

  1. 带有default constructor 的member class object
  2. 带有default constructor 的base class
  3. 带有一个virtual function 的class
  4. 带有一个virtual base class 的class

如果有已经存在的constructor,那么会在用户自己的代码之前安插编译器需要的代码,以完成一些初始化。

c++新手的两个常见的误解:

  1. 任何class如果没有定义default constructor,就会被合成出来一个
  2. 编译器合成出来的default constructor 会显示设定class内每一个data member的默认值

2.2 copy constructor 的构造操作

copy constructor 在必要的时候才有编译器产生出来

以下四种情况,一个class不展现出bitwise copy semantics,即编译器会为其合成一个default copy constructor:

  1. class内含一个member object,而后者的class声明有一个copy constructor(可以是设计者显示声明的,也可以是编译器合成的)
  2. class继承自一个base class,而后者存在一个copy constructor(可以是显示声明也可是合成的)
  3. class 声明了一个或多个virtual function(为设定正确的vptr)
  4. class 派生自一个继承串链,其中有一个或多个virtual base classes为(设定正确的base class的偏移值)

2.3 程序转化语义学

在严谨的c++用词中,“定义”是指“占用内存”的行为

NRV优化:省去了局部变量,减少了一次拷贝构造的开销
有了拷贝构造函数,不能bitwise,较慢,故编译器会进行NRV优化

在使用memcpy()或memset时,要注意class内部是有含有vptr,vbtr

2.4 成员初始化列表(member initialization list)

为了让程序正确编译,一下四种情况,必须使用成员初始化列表:

  1. 当初始化一个reference member时(c++11在成员声明的时候可以直接设定初值,而不需要再函数中,下同)
  2. 当初始化一个const member时
  3. 当调用一个base class 的constructor,而它拥有一组参数时
  4. 当调用一个member class的constructor,而它拥有一组参数时

编译器会在constructor中以适当的顺序安插初始化列表,且在用户代码之前,list中项目的安插顺序以members的声明顺序决定。


可以调用一个member function 以设定一个member的初值,这种方法是合法的,因为在进入constructor的时候this指针已经明确:

//X::xfool()被调用
X::X(int val) : i(xfoo(val)), j(val) {

}

子类的成员函数被用作父类的构造函数的参数:

class FooBar : public X { 
 int _fval; 
public: 
 int fval() { return _fval; } 
 FooBar( int val ) 
 : _fval( val ), 
 X( fval() ) //先调用父类的构造函数,但是此时val还未设定初值,故结果不是想要的
 {} 
 ... 
};

猜你喜欢

转载自www.cnblogs.com/senshaw/p/10991116.html