C++ 构造函数和成员列表

构造函数

构造函数是一种可初始化其类的实例的成员函数。——MSDN

在派生类的构造函数中,基类和成员构造函数的调用顺序如下。 首先,调用基构造函数,然后按照基类成员在类声明中出现的顺序对这些成员进行初始化,所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中;然后,调用派生构造函数。

  • 例子
#include   
using namespace std;  

class Contained1 {  
public:  
    Contained1() {  
        cout << "Contained1 constructor." << endl;  
    }  
};  

class Contained2 {  
public:  
    Contained2() {  
        cout << "Contained2 constructor." << endl;  
    }  
};  

class Contained3 {  
public:  
    Contained3() {  
        cout << "Contained3 constructor." << endl;  
    }  
};  

class BaseContainer {  
public:  
    BaseContainer() {  
        cout << "BaseContainer constructor." << endl;  
    }  
private:  
    Contained1 c1;  
    Contained2 c2;  
};  

class DerivedContainer : public BaseContainer {  
public:  
    DerivedContainer() : BaseContainer() {  
        cout << "DerivedContainer constructor." << endl;  
    }  
private:  
    Contained3 c3;  
};  

int main() {  
    DerivedContainer dc;  
    int x = 3;  
}  

输出:

Contained1 constructor.         //1
Contained2 constructor.         //2
BaseContainer constructor.      //3
Contained3 constructor.         //4
DerivedContainer constructor.   //5

对于本例来说,派生类DerivedContainer 首先调用基类BaseContainer的构造方法,基类的构造方法会根据成员在基类中出现的顺序初始化类型为class的成员c1c2,所以首先输出的1,2,然后在执行基类构造方法的函数体,输出3。基类完事之后,派生类初始化自己的成员,输出4,接着执行派生类自己的函数体,输出5。

成员列表(Member Lists)

使用成员初始值设定项列表从构造函数参数初始化类成员。 此方法使用直接初始化,这比在构造函数体内使用赋值运算符更高效

例子

#include 

struct A {
  A() { std::cout << "A::A()\n"; }
  A(int) { std::cout << "A::(int)\n"; }
  void operator=(const A&) { std::cout << "A::operator=(const A&)\n"; }
};

struct C1 {
  A a;
  C1(int i) { 
    a = i;
  }
};

struct C2 {
  A a;
  C2(int i)  : a(i) {}
};

int main() {
  std::cout << "How expesive is it to create a C1?\n";
  { C1 c1(7); }
  std::cout << "How expensive is it to create a C2?\n";
  { C2 c2(7); }
}

输出:

How expesive is it to create a C1?
A::A()
A::(int)
A::operator=(const A&)
How expensive is it to create a C2?
A::(int)

Thus, the cost of not using an initializer is: one default constructor, one int constructor, and one assignment operator.

对于赋值操作,a = i,构造函数A()生成左值a,构造函数A(int)生成右值i,然后赋值。如果使用成员列表初始化,则直接调用构造函数A(int)

不能使用成员列表的情况

class A {
public:
    A(int i) : n_(i){}
protected:
    int n_;
};

class B : public A {
public:
    B() : n_(0)
    {}
};

编译后报错:

error: class 'B' does not have any field named 'n_'

虽然基类A的成员n_可以被派生类B继承,但是不能通过成员列表初始化。派生类的构造函数中,并不直接初始化基类的成员,而是调用基类的构造函数初始化基类的部分。
派生类B首先会调用基类A的构造方法,成员n_在基类中被初始化,随后如果在派生类B的成员列表初始化,那么同一个对象就会被初始化两次,所以基类的成员在派生类的初始化阶段是不可见的。

正确写法:

class A {
public:
    A(int i) : n_(i){}
protected:
    int n_;
};

class B : public A {
public:
    B() : A(0) {}
};

你可能感兴趣的:(随笔记)