c++类构造函数理解

来源:互联网 发布:算法4 epub 编辑:程序博客网 时间:2024/05/17 05:13

记录一下我对C++类构造函数的理解。

首先,构造函数分成两种,默认构造函数和非默认构造函数(好吧,就这么叫它)。

默认构造函数只能有一个,如果没有自定义构造函数,那么编译器将自动生成一个默认构造函数,当然这个构造函数不会做任何事情。如果程序定义了构造函数(包括默认和非默认),编译器都不再自动提供默认构造函数。

如class C, 对应自动生成的默认构造函数为C() { };

程序员可以自定义默认构造函数,而且只能定义一个默认构造函数。如定义class A.

Class A

{

    public:

//       A() { }  //constructor1, do nothing,默认构造函数

//       A(int m=1):v1(m);//construct2,默认构造函数,提供了部分的默认初始化值

//       A(int m=1, int n=2):v1(m), v2(n);//construct3, 默认构造函数,提供了所有的默认初始化值

//       A(int m); //construct4,非默认构造函数, 提供一个参数

//       A(int m, int n); //construct5, 非默认构造函数, 提供两个参数


    private:

        int v1;

        int v2;

}

下面具体分析一下各种定义方式会带来的问题:

1)不定义任何构造函数,这个上面说过,编译器会自动构造一个默认构造函数,等价于定义了construct1。

2) 只定义construct2, 可以正确编译运行。但是无法以形式A a(1,2)定义对象,这是显然的。

3)    只定义construct3, 可以正确编译运行。

4)    定义2个及以上默认构造函数的情况。

       想来同时提供construct2和construct3是不会矛盾的,而且符合重载的要求,但是事实并非如此。

       同时定义construct2和construct3,  以形式 A a;定义对象将会导致编译器报错。

       error: call of overloaded ‘A()’ is ambiguous
       note: candidates are: A::A(int, int)
       note:                              A::A(int)

       以形式A a(1);定义对象将会导致报错:

        error: call of overloaded ‘A(int)’ is ambiguous
        note: candidates are: A::A(int, int)
        note:                 A::A(int)

        note:                 A::A(const A&)

        然而,以形式 A a(1,2);定义对象则可以正确通过编译并运行。   这是什么原因?编译器工作的原理是什么?不知道....

5) 从重载的角度分析,construct2和construct3签名一致,construct4和construct5签名一致,不能同时定义。实验结果证实确实如此。

6) construct4和5恰为构造函数重载的典型实例,当然能正确编译运行。

7 )  只提供非默认构造函数,只有construct4或5或同时有4和5而无1/2/3

       这将导致无法使用 A  a;的形式来声明对象。因为以A a的形式声明对象是调用默认构造函数来构造对象的。编译器找不到默认构造函数自然会报错。

       与此同时对象数组的声明A  a[10];也将得到报错,原理同上。

      那么如下声明并初始化对象数组会是什么结果呢?

      A  a[3] = {

           A(1,1),  A(2,2), A(3,3)

     }   //(再只定义了construct5的情况下)

      在C++ Primer Plus中有如下语句:“要创建类对象数组,则这个类必须有默认构造函数。”,“初始化对象数组的方案位,首先使用默认构造函数创建数组元素,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应的元素中。因此要创建类对象数组,则这个类必须有默认构造函数。”

      在我看来这话不尽然正确,或许Stephen写书的时候使用的编译器是如他所说,但是我在g++中做的实现不是如此。上述对象数组的定义在g++下能够通过编译,并运行正确。g++的策略是,如果花括号中明确调用了构造函数来初始化对象,且该构造函数已被定义,那么将直接使用构造函数来创建对象元素,无临时对象和复制的过程。然而,如果花括号中有任一元素没有显示的使用已定义的构造函数初始化,那么编译器将调用默认构造函数来初始化该对象元素,这时如果没有定义默认构造函数,将无法通过编译。

      那么在上述情形中   A  a[4] = { A(1,1), A(2,2), A(3,3)}, 将无法通过编译,因为A[3]实际上要通过调用默认构造函数来初始化,而这里又没有定义默认构造函数。


关于默认构造函数的调用。

实际上在g++中,未提供任何构造函数时,初始化一个对象,编译器并不去调用自动提供的那个什么也不做的构造函数,而仅仅是在内存中为对象分配了空间。(这应该是编译器的优化手段,不同编译器不同。)如果程序自己定义了一个A(){};虽然也什么都不做,但是初始化对象时,该构造函数却会被执行。这就带来了额外的开销,所以如果真的不定义任何构造函数的话,那么就干脆也别定义A(){};这么个空构造函数了,无意义,反而降低效率。


构造函数的参数传递,参数的第一个值为隐藏的this指针,指向对象的首地址,其次才是传进去的参数。

类对象的大小:无任何属性域的对象,大小为1, 即类class A{};  有属性域的类,大小为属性大小之和。this指针不占内存。





原创粉丝点击