C++构造函数语义——默认构造函数

原创 2012年03月26日 15:19:20

0、前言


  《The C++ ARM》告诉我们:“默认构造函数会在需要的时候自动生成(被编译器)。”然后“在需要的时候”是一个很抽象的概念,本文主要描述的问题也正是这些需要的情况。

  我们看看下面的代码片段:

class Foo
{  
public:
    int val;
    Foo *pnext;
};
void foo_bar()
{  
    Foo bar;
    if (bar.val || bar.pnext)
    {
        cout << bar.val << endl;
        cout << bar.pnext << endl;
    }
}
用户并没有显示地定义默认构造函数,编译器会为它自动生成一个无关紧要(trivial)的默认构造函数,生成的默认构造函数什么也不错,既不会讲其成员变量置零,也不会做其他的任何事情,只是为了保证程序能够正确运行而已,这就是所谓的“需要”,如果还需要给初始化成员变量,这件事情还是交给程序员做吧!


一、非平凡(non-trivival)默认构造函数


C++标准描述了哪些情况,这样的隐式默认构造函数是无关紧要的。一个非平凡(non-trivival)的默认构造函数是ARM中所说的被实现所“需要”,并在必要的时候被编译器自动生成。下面来看看默认构造函数是非平凡的四种情况:


1.1 含有包含默认构造函数的成员类对象


如果该类包含一个成员类对象,它有默认的构造函数,那么这个类的隐式构造函数是非平凡的,并且编译器需要为包含的这个成员类对象生成一个默认构造函数。然后,这个编译器生成的默认构造函数只有在实际上被调用时才会被真正的生成。

class Foo
{
public:

    Foo(){ _i = 1; }
    int _i;
};
class Bar
{
public:
    Foo foo;
    char *str;
};
void foo_bar()
{
    Bar bar;
}

  在这个程序片段中Bar的成员foo含有默认构造函数,它初始化自己的类成员_i为1而Bar本身并没有定义默认的构造函数,这个构造函数的目的是为了初始化它的成员变量foo,实际上就是调用Bar::foo的默认构造函数,但它并不会做一丁点关于另外一个变量str的初始化和赋值工作,初始化Bar::foo是编译器的责任,二初始化str是程序员的。我们可以用以下代码来大致描述一下编译器的工作:

inline 
Bar::Bar() 
{ 
   // Pseudo C++ Code 
   foo.Foo::Foo(); 
}

  如果这里的Bar含有默认构造函数呢?我们可以从编译器和程序员的责任划分来考虑这个问题:

  1. Bar的默认构造函数调用foo的默认构造函数,那么编译器啥也不用做了。

  2. Bar的默认构造函数中没有去foo这个成员变量,那么编译器需要去帮助程序员把这件事情做完,插入一条类似“foo.Foo::Foo();”的代码。

注:如果Bar含有多个成员类变量,则编译器会按照这些变量的声明顺序去做以上处理。


1.2 含有包含默认构造函数的基类


  类似的,要是一个继承的基类包含默认构造函数而该类本身没有任务的构造函数,那么编译器会生成一个默认构造函数,目的是初始化它的基类。

  当程序员为该类定义了多个构造函数,就是没定义默认构造函数呢?

  在这种情况下,编译器会在每一个构造函数中增加(augment)有关调用基类的默认构造函数部分代码。


1.3 含有虚函数


  生成的默认构造函数是必须的当另外两个额外条件(满足其一):

  1.该类定义了(或继承了)虚函数。

  2.在该类的继承关系中,有一个或更多的虚基类。

  可以参考一下的类继承关系:

class Widget
{
public:
    virtual void flip() = 0;
};
class Bell: public Widget
{
public:
    void flip(){ cout <<"Bell." << endl; }
};
class Whistle: public Widget
{
public:
    void flip(){ cout <<"Whistle." << endl; }
};

void flip(Widget &widget)
{
    widget.flip();
}

void foo()
{
    Bell b;
    Whistle w;
    flip(b);
    flip(w);
}

  在编译时,在默认构造函数中会发生下面的两个类扩充(augmentation):

  1.虚表会被产生,其内容被这个类的活动(active)虚函数填充。

  2.编译器为每个类对象生成一个虚指针(vtbl)。


1.4 含有一个虚基类


  关于虚基类的内容,本文暂不详述,需要记住的是:编译器会为该类添加一个类似虚指针的东西——“_vbcX”。


二、参考文献


  《Inside The C++ Object Model》


相关文章推荐

默认构造方法浅析

1、我们在定义一个类时,如果没有为该类定义构造方法,系统会为之提供一个无参的默认构造方法;如果我们为该类自定义了一个带参数的构造方法,那么默认的构造方法将不会自动提供,下面的代码将不会通过编译: ...
  • leiswpu
  • leiswpu
  • 2011年03月13日 14:06
  • 2114

C++默认构造函数——深入理解

错误认识1:若程序员没有自己定义无参数的构造函数,那么编译器会自动生成默认构造函数,来进行对成员函数的初始化。 错误认识2:编译器合成出来的default constructor会明确设定'“cla...

浅拷贝——C++编译器默认提供的拷贝构造函数

浅拷贝与深拷贝区别:在执行拷贝动作时,浅拷贝只会执行简单的赋值动作。遇到类成员变量是指针类型时,由于会直接将指针本身拷贝过去,导致有几个指针指向同一片内存,以致于最后在调用析构函数时会对同一片内存区域...

C/C++—— 默认构造函数是否会执行

在我了解默认构造函数之前,我一直以为两点:1.如果类中我不声明构造函数,编译器会自动生成默认构造函数并执行。 2.编译器自动生成默认构造函数会将类成员初始化。然而这两点都是错的。...

C++默认构造函数——深入理解

ZZ地址 :http://blog.csdn.net/generalhking/article/details/7947989 错误认识1:若程序员没有自己定义无参数的构造函数,那么编译器会自动生成...

C++构造函数

  • 2014年06月16日 13:10
  • 10KB
  • 下载

C++中拷贝构造函数的使用

  • 2012年07月29日 08:56
  • 275KB
  • 下载

c++——复制构造函数的使用

#include #include #include using namespace std; struct student { student(){ cout

c++ 类的定义 和构造函数

  • 2012年03月24日 22:06
  • 11KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++构造函数语义——默认构造函数
举报原因:
原因补充:

(最多只允许输入30个字)