默认构造函数_默认构造函数怎么写 - CSDN
精华内容
参与话题
  • C++默认构造函数

    2019-02-22 21:05:37
    默认构造函数 默认的构造函数是指为所有参数都提供了默认值的构造函数,通常是指无参的构造函数。比如下面的类Test,它的默认构造函数就是Test()。 class Test { public: Test(){} // default constructor } ...

    1.默认构造函数
    默认的构造函数是指为所有参数都提供了默认值的构造函数,通常是指无参的构造函数。比如下面的类Test,它的默认构造函数就是Test()。

    class Test
    {
    public:
        Test(){} // default constructor
    } ;
    

    如果你没有为你的类提供任何构造函数,那么编译器将自动为你生成一个默认的无参构造函数。一旦你为你的类定义了构造函数,哪怕只是一个,那么编译器将不再生成默认的构造函数。
    2.为你的类提供默认的构造函数
    有很多原因,列举如下:

    1. 当你使用静态分配的数组,而数组元素类型是某个类的对象时,就要调用默认的构造函数,比如下面的代码。
      Object buffer[10]; // call default constructor
    2. 当你使用动态分配的数组,而数组元素类型是某个类的对象时,就要调用默认的构造函数,比如下面的代码,如果Object没有默认的构造函数,是无法通过编译的,因为new操作符要调用Object类的无参构造函数类初始化每个数组元素。
      在Object* buffer = new Object[10];
    3. 当你使用标准库的容器时,如果容器内的元素类型是某个类的对象时,那么这个类就需要默认的构造函数,原因同上。
      vector<Object> buffer;
    4. 一个类A以另外某个类B的对象为成员时,如果A提供了无参构造函数,而B未提供,那么A则无法使用自己的无参构造函数。下面的代码将导致编译错误。
    class B
    {
        B(int i){}
    };
    
    class A
    {
        A(){}
        B b;
    };
    
    int main(void) 
    { 
        A a(); // error C2512: 'B' : no appropriate default constructor available
        getchar() ; 
        return 0 ; 
    } 
    

    再比如下面的代码,类A定义了拷贝构造函数,而没有提供默认的构造函数,B继承自A,所以B在初始化时要调用A的构造函数来初始化A,而A没有默认的构造函数,故产生编译错误。

    class A
    {
        A(const A&){}
    };
    
    class B : public A
    {
        
    };
    
    int main(void) 
    { 
        B b; //error C2512:'B': no appropriate default constructor available
    
        getchar() ; 
        return 0 ; 
    } 
    
    展开全文
  • 什么是默认构造函数

    千次阅读 2017-05-29 01:04:40
    对于C++默认构造函数,我曾经有两点误解: 类如果没有定义任何的构造函数,那么编译器(一定会!)将为类定义一个合成的默认构造函数。 合成默认构造函数会初始化类中所有的数据成员。 第一个误解来自于我学习C++的第...

    对于C++默认构造函数,我曾经有两点误解:

    • 类如果没有定义任何的构造函数,那么编译器(一定会!)将为类定义一个合成的默认构造函数。
    • 合成默认构造函数会初始化类中所有的数据成员。

    第一个误解来自于我学习C++的第一本书 《C++ Primer》,在书中392页:“只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数”。

    实际上这句话也没有说错,它说明了默认构造函数定义的必要非充分条件,然而却给当时初学C++的我造成了一定的误解。

    第二个误解依旧来自于Primer中的一句话:“合成的默认构造函数使用与变量初始化相同的规则来初始化成员。具有类类型的成员通过运行各自的默认构造函数来进行初始化”。然而这也是我理解的片面,因为Primer也说到了:“如果类包含内置或复合类型的成员,则该类不应该依赖于合成的默认构造函数”,言下之意就是合成的默认构造函数并不会初始化内置或复合类型的成员。

    总结了我有这些误解的原因,第一是初学时知识体系没形成,对Primer中所说的内容没有真正的理解,第二就是Primer在某种程度上的确不是C++初学者能看懂的书,或许看时觉得懂了,却是遗漏了很多知识。也说明了Primer 是座宝库,常常回顾将会有新的感悟。

    让我对上面两个观点产生疑惑,是在看《Effective C++》时,条款05《了解C++默认编写并调用哪些函数》中说到“….惟有当这些函数被需要(被调用),它们才会被编译器创建出来。” (“这些函数“指的是编译器版本的复制构造函数、赋值操作符和析构函数,还包括了默认构造函数。)也就是说,默认构造函数“被需要”的时候编译器才会帮我们合成,那什么情况才是默认构造函数”被需要“呢?这个问题《Effective C++》并没有给出答案,直到看了《深度探索C++对象模型》,才明白了编译器何时才会帮我们合成一个默认构造函数。

    我写这篇文章的目的是给和我有同样误解或疑惑的C++初学者看的,如果你对合成默认构造函数已有充分的认识,请忽略本文的内容。

    什么是默认构造函数?

    默认构造函数是可以不用实参进行调用的构造函数,它包括了以下两种情况:

    1. 没有带明显形参的构造函数。
    2. 提供了默认实参的构造函数。

    类设计者可以自己写一个默认构造函数。编译器帮我们写的默认构造函数,称为“合成的默认构造函数”。

    强调“没有带明显形参”的原因是,编译器总是会为我们的构造函数形参表插入一个隐含的this指针,所以”本质上”是没有不带形参的构造函数的,只有不带明显形参的构造函数,它就是默认构造函数。

    默认构造函数什么时候被调用?

    如果定义一个对象时没有提供初始化式,就使用默认构造函数。

    例如:

    class A
    {
    public:
        A(bool _isTrue= true, int _num=10){ isTrue = isTrue; num = _num; }; //默认构造函数
        bool isTrue;
        int num;
    
    };
    int main()
    {
        A a; //调用类A的默认构造函数
    }

    理解“被需要”这三个字

    前面提到在《Effective C++》中指出惟有默认构造函数”被需要“的时候编译器才会合成默认构造函数。关键字眼是”被需要“。被谁需要?做什么事情?像下面这段代码,默认构造函数”被需要“了吗?

    class A
    {
    public:
        bool isTrue;
        int num;
    
    };
    int main()
    {
        A a;
        if (a.isTrue)
            cout << a.num;
        return 0;
    }

    你可能认为这里定义类对象a的时候没有提供参数且A没有定义默认构造函数,编译器肯定是合成了一个默认构造函数并调用它来初始化A的数据成员,实则不是。当你试图查看合成默认构造函数把数据成员num初始化为什么值的时候,你会发现编译器甚至都让你运行不了程序:

    img

    当类只含有内置类型或复合类型的成员时,编译器是不会为类合成默认构造函数的,这种类并不符合”被需要“的条件,甚至当类满足“被需要”条件,编译器合成了默认构造函数时,类中内置类型与复合类型数据成员依然不会在默认构造函数中进行初始化。Primer中也有提到:“如果类包含内置或复合类型的成员,则该类不应该依赖于合成的默认构造函数“。

    上面代码中,默认构造函数”被需要“是对程序来说的,程序需要isTrue被初始化以便可以进行条件判断,需要num被初始化以便可以输出。然而这种需要并不会促使编译器合成默认构造函数。惟有被编译器所需要时,编译器才会合成默认构造函数。那怎样的类才是编译器需要合成默认构造函数的呢?

    总结:

    1. 合成默认构造函数总是不会初始化类的内置类型及复合类型的数据成员。
    2. 分清楚默认构造函数被程序需要与被编译器需要,只有被编译器需要的默认构造函数,编译器才会合成它。

    何时默认构造函数才会被编译器需要?

    以下四种情况的类,编译器总是需要默认构造函数完成某些工作:

    1. 含有类对象数据成员,该类对象类型有默认构造函数。

    如果一个类没有任何构造函数,但是它含有一个类对象数据成员,且该类对象类型有默认构造函数,那么编译器就会为该类合成一个默认构造函数,不过这个合成操作只有在构造函数真正需要被调用的时候才会发生。举个例子,编译器将为类B合成一个默认构造函数:

    class A
    {
    public:
        A(bool _isTrue=true, int _num = 0){ isTrue = _isTrue; num = _num; }; //默认构造函数
        bool isTrue;
        int num;
    
    };
    class B
    {
    public:
        A a;//类A含有默认构造函数
        int b;
        //...
    };
    int main()
    {
        B b;    //编译至此时,编译器将为B合成默认构造函数
        return 0;
    }

    被合成的默认构造函数做了什么事情?大概如下面这样:

    B::B()
    {
        a.A::A();
    }

    被合成的默认构造函数内只含必要的代码,它完成了对数据成员a的初始化,但不产生任何代码来初始化B::b。正如上面所说,初始化类的内置类型或复合类型成员是程序的责任而不是编译器的责任。为了满足程序的需要,我们一般会自己写构造函数来对B::b进行初始化,像这样:

    B::B()
    {
        a.A::A(); //编译器插入的代码
        b = 0;      //显示定义的代码
    }

    如果类中有多种类对象成员,则编译器按照这些类对象成员声明的顺序,在构造函数按顺序插入调用各个类默认构造函数的代码。

    2.基类带有默认构造函数的派生类。

    当一个类派生自一个含有默认构造函数的基类时,该类也符合编译器需要合成默认构造函数的条件。编译器合成的默认构造函数将根据基类声明顺序调用上层的基类默认构造函数。同样的道理,如果设计者定义了多个构造函数,编译器将不会重新定义一个合成默认构造函数,而是把合成默认构造函数的内容插入到每一个构造函数中去。

    3. 带有虚函数的类  

      类带有虚函数可以分为两种情况:

    1. 类本身定义了自己的虚函数
    2. 类从继承体系中继承了虚函数(成员函数一旦被声明为虚函数,继承不会改变虚函数的”虚性质“)。

    这两种情况都使一个类成为带有虚函数的类。这样的类也满足编译器需要合成默认构造函数的类,原因是含有虚函数的类对象都含有一个虚表指针vptr,编译器需要对vptr设置初值以满足虚函数机制的正确运行,编译器会把这个设置初值的操作放在默认构造函数中。如果设计者没有定义任何一个默认构造函数,则编译器会合成一个默认构造函数完成上述操作,否则,编译器将在每一个构造函数中插入代码来完成相同的事情。

    4.带有虚基类的类

    虚基类的概念是存在于类与类之间的,是一种相对的概念。例如类A虚继承于类X,则对于A来说,类X是类A的虚基类,而不能说类X就是一个虚基类。虚基类是为了解决多重继承下确保子类对象中每个父类只含有一个副本的问题,比如菱形继承。如下图:

    img

    于是,类A对象中含有一份类X对象,类C中也含有一份类X对象,当我们遇上如下代码时:

    class X  { public: int i; };
    class A : public virtual X{ public:int j; };
    class B : public virtual X{ public:double d; };
    class C : public A, public B{ public: int k; };
    
    void function(A *pa)
    {
        pa->i = 1000;
    }
    int main()
    {
        A *a= new A();
        C *c= new C();
        function(a);  //关注重点在这里
        function(c);     //关注重点在这里
        return 0;
    }

    函数function参数pa的真正类型是可以改变的,既可以把A对象指针赋值给pa,也可以把对象指针赋值给pa,在编译阶段并无法确定pa存储的i是属于A还是C的虚基类对象。为了解决这问题,编译器将产生一个指向虚基类X的指针,使得程序得以在运行期确定经由pa而存取的X::i的实际存储位置。这个指针的安插,编译器将会在合成默认构造函数中完成,同样的,如果设计者已经写了多个构造函数,那么编译器不会重新写默认构造函数,而是把虚基类指针的安插代码插入已有的构造函数中。

    总结

    重新强调文章开篇所提,以下两个观点都是误解:

    a) 任何类如果没有定义构造函数,则编译器会帮我们合成一个默认构造函数。

    b) 合成默认构造函数会对类中的每一个数据成员进行初始化。

    只有在编译器需要默认构造函数来完成编译任务的时候,编译器才会为没有任何构造函数的类合成一个默认构造函数,或者是把这些操作插入到已有的构造函数中去。

    编译器需要默认构造函数的四种情况,总结起来就是:

    a) 调用对象成员或基类的默认构造函数。

    b) 为对象初始化虚表指针与虚基类指针。

    展开全文
  • 默认构造函数详解

    千次阅读 2018-05-10 11:20:26
    错误认识1:若程序员没有自己定义无参数的构造函数,那么编译器会自动生成默认构造函数,来进行对成员函数的初始化。错误认识2:编译器合成出来的default constructor会明确设定'“class内每一个data member的默认值...

    错误认识1:若程序员没有自己定义无参数的构造函数,那么编译器会自动生成默认构造函数,来进行对成员函数的初始化。

    错误认识2:编译器合成出来的default constructor会明确设定'“class内每一个data member的默认值”。但这两种种认识是有误的,不全面的。

    正确认识:

    默认的构造函数分为有用的和无用的,所谓无用的默认构造函数就是一个空函数、什么操作也不做,而有用的默认构造函数是可以初始化成员的函数。
    对构造函数的需求也是分为两类:一类是编辑器需求,一类是程序的需求。
    程序的需求:若程序需求构造函数时,就是要程序员自定义构造函数来显示的初始化类的数据成员。
    编辑器的需求:编辑器的需求也分为两类:一类是无用的空的构造函数(trivial),一类是编辑器自己合成的有用的构造函数(non-trivival)。

    在用户没有自定义构造函数的情况下:

    一、由于编辑器的需求,编辑器会调用空的无用的默认构造函数。如:类中没有显式定义任何构造函数。

    二、但在某些情况下,编辑器就一定会合有用的默认构造函数。

    1.无关紧要(trivial)的默认构造函数【无用构造函数】

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

    1. #include<iostream>   
    2. using namespace std;  
    3. class Foo  
    4. {   
    5. public:   
    6.   int val;   
    7.   Foo *pnext;  
    8. };  
    9. void foo_fo(){  
    10.  Foo fo;   
    11. if (fo.val || fo.pnext)   
    12.    {   
    13.       cout << fo.val << endl;   
    14.      cout << fo.pnext << endl;   
    15.    }  
    16. }  
    17. int main()  
    18. {   
    19. foo_fo();   
    20. system("pause");   
    21. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Foo  
    4. {   
    5. public:   
    6.   int val;   
    7.   Foo *pnext;  
    8. };  
    9. void foo_fo(){  
    10.  Foo fo;   
    11. if (fo.val || fo.pnext)   
    12.    {   
    13.       cout << fo.val << endl;   
    14.      cout << fo.pnext << endl;   
    15.    }  
    16. }  
    17. int main()  
    18. {   
    19. foo_fo();   
    20. system("pause");   
    21. }  

    如果基类和继承类都没有自定义的构造函数。那么,都会生成trival的默认构造函数。例:

    1. #include<iostream>   
    2. using namespace std;   
    3. #include<iostream>    
    4. using namespace std;   
    5. class Foo   
    6. {   
    7.   private:   
    8.   int val;   
    9. };   
    10. class Bar:public Foo   
    11. {   
    12. public:   
    13.   char *str;   
    14.   int i;   
    15. };   
    16. void foo_bar()   
    17. {   
    18.    Bar bar;   
    19.    cout<<bar.i<<endl;   
    20. if (bar.str )   
    21. {   
    22.    cout << "Print the content !" << endl; }  
    23. }   
    24. int main()   
    25. {   
    26.   foo_bar();   
    27.   system("pause");   
    28.   return 0;   
    29. }  
    1. #include<iostream>  
    2. using namespace std;   
    3. #include<iostream>   
    4. using namespace std;   
    5. class Foo   
    6. {   
    7.   private:   
    8.   int val;   
    9. };   
    10. class Bar:public Foo   
    11. {   
    12. public:   
    13.   char *str;   
    14.   int i;   
    15. };   
    16. void foo_bar()   
    17. {   
    18.    Bar bar;   
    19.    cout<<bar.i<<endl;   
    20. if (bar.str )   
    21. {   
    22.    cout << "Print the content !" << endl; }  
    23. }   
    24. int main()   
    25. {   
    26.   foo_bar();   
    27.   system("pause");   
    28.   return 0;   
    29. }  


    2.非平凡(non-trivival)默认构造函数【有用构造函数】

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

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

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

    如果一个class中含有成员对象,而且这个对象有default constructor(如Foo foo,Foo类中有default constructor), 那么编译器就会给这个class(Bar)合成一个default constructor, 但是这个合成动作只有在调用需要时才会产生。也就是说,在需要时才会合成。

    1. #include<iostream>    
    2. using namespace std;   
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo();   
    7.   Foo( int );  
    8. private:    
    9.   int val;   
    10. };   
    11. Foo::Foo()   
    12. {   
    13.   cout << "Call Foo::Foo() Constructor !"<< endl;   
    14.   val = 0;   
    15. }   
    16. Foo::Foo(int i)   
    17. {   
    18.   cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    19.   val = i;   
    20. }   
    21. class Bar   
    22. {   
    23. public:  
    24.   Foo foo;   
    25.   char *str;   
    26. };  
    27. void foo_bar()   
    28. {   
    29.   Bar bar; // Bar::foo must be initialized here if (bar.str )    
    30.   { cout << "Print the content !" << endl; }   
    31. }   
    32. int main()   
    33. {   
    34.   foo_bar();   
    35.   system("pause");   
    36.   return 0;   
    37. }  
    1. #include<iostream>   
    2. using namespace std;   
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo();   
    7.   Foo( int );  
    8. private:    
    9.   int val;   
    10. };   
    11. Foo::Foo()   
    12. {   
    13.   cout << "Call Foo::Foo() Constructor !"<< endl;   
    14.   val = 0;   
    15. }   
    16. Foo::Foo(int i)   
    17. {   
    18.   cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    19.   val = i;   
    20. }   
    21. class Bar   
    22. {   
    23. public:  
    24.   Foo foo;   
    25.   char *str;   
    26. };  
    27. void foo_bar()   
    28. {   
    29.   Bar bar; // Bar::foo must be initialized here if (bar.str )   
    30.   { cout << "Print the content !" << endl; }   
    31. }   
    32. int main()   
    33. {   
    34.   foo_bar();   
    35.   system("pause");   
    36.   return 0;   
    37. }  


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

    我们可以用以下代码来大致描述一下编译器的工作:

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

    结论:如果class中内含一个以上的含有default constructor的object,那在为class合成的default constructor中,会按照object的声明次序调用object 的 default constructor

    如果Bar中用户自定义的默认构造函数(用户有定义,则编译器不会再自动生成):

    1. #include<iostream>    
    2. using namespace std;  
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo();   
    7.   Foo( int );   
    8. private:   
    9.   int val;   
    10. };  
    11. Foo::Foo()   
    12. {   
    13.   cout << "Call Foo::Foo() Constructor !"<< endl;   
    14.   val = 0;   
    15. }   
    16. Foo::Foo(int i)   
    17. {   
    18.   cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    19.   val = i;   
    20. }   
    21. class Bar   
    22. {   
    23. public:   
    24.   Foo foo;   
    25.   char *str;   
    26.   Bar(){}//默认构造函数    
    27. };   
    28. void foo_bar()   
    29. {   
    30.   Bar bar;   
    31. if (bar.str ) {  
    32.   cout << "Print the content !" << endl;   
    33. }   
    34. }   
    35. int main()   
    36. {   
    37.   foo_bar();   
    38.   system("pause");  
    39.   return 0;   
    40. }  
    1. #include<iostream>   
    2. using namespace std;  
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo();   
    7.   Foo( int );   
    8. private:   
    9.   int val;   
    10. };  
    11. Foo::Foo()   
    12. {   
    13.   cout << "Call Foo::Foo() Constructor !"<< endl;   
    14.   val = 0;   
    15. }   
    16. Foo::Foo(int i)   
    17. {   
    18.   cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    19.   val = i;   
    20. }   
    21. class Bar   
    22. {   
    23. public:   
    24.   Foo foo;   
    25.   char *str;   
    26.   Bar(){}//默认构造函数   
    27. };   
    28. void foo_bar()   
    29. {   
    30.   Bar bar;   
    31. if (bar.str ) {  
    32.   cout << "Print the content !" << endl;   
    33. }   
    34. }   
    35. int main()   
    36. {   
    37.   foo_bar();   
    38.   system("pause");  
    39.   return 0;   
    40. }  

    结果一样:

    对比,以下代码不符合上述要求,会报错:

    1. #include<iostream>    
    2. using namespace std;   
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo(); //去掉默认构造函数会报错    
    7.   Foo( int );   
    8. private:   
    9.   int val;  
    10. };   
    11. Foo::Foo()   
    12. {   
    13.   cout << "Call Foo::Foo() Constructor !"<< endl;   
    14.   val = 0;   
    15. }   
    16. Foo::Foo(int i)   
    17. {   
    18.   cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    19.   val = i;   
    20. }   
    21. class Bar   
    22. {   
    23. public:   
    24.   Foo foo(1); //不是默认构造函数产生的成员,也会报错。expected `;' before '(' token char *str;    
    25. };   
    26. void foo_bar()   
    27. {   
    28.   Bar bar; // Bar::foo must be initialized here    
    29. if (bar.str )   
    30. {   
    31.   cout << "Print the content !" << endl; }   
    32. }   
    33. int main()   
    34. {   
    35.   foo_bar();   
    36.   system("pause");   
    37.   return 0;   
    38. }   
    1. #include<iostream>   
    2. using namespace std;   
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo(); //去掉默认构造函数会报错   
    7.   Foo( int );   
    8. private:   
    9.   int val;  
    10. };   
    11. Foo::Foo()   
    12. {   
    13.   cout << "Call Foo::Foo() Constructor !"<< endl;   
    14.   val = 0;   
    15. }   
    16. Foo::Foo(int i)   
    17. {   
    18.   cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    19.   val = i;   
    20. }   
    21. class Bar   
    22. {   
    23. public:   
    24.   Foo foo(1); //不是默认构造函数产生的成员,也会报错。expected `;' before '(' token char *str;   
    25. };   
    26. void foo_bar()   
    27. {   
    28.   Bar bar; // Bar::foo must be initialized here   
    29. if (bar.str )   
    30. {   
    31.   cout << "Print the content !" << endl; }   
    32. }   
    33. int main()   
    34. {   
    35.   foo_bar();   
    36.   system("pause");   
    37.   return 0;   
    38. }   

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

    #include<iostream>using namespace std; #include<iostream> using namespace std; class Foo { public: Foo() { cout << "Call Foo::Foo() Constructor !"<< endl; val = 0; } Foo(int i) { cout << "Call Foo::Foo(int i) Constructor !"<< endl; val = i; } private: int val; }; class Foo1 { public: Foo1() { cout << "Call Foo1::Foo1() Constructor !"<< endl; val1 = 0; } Foo1(int i) { cout << "Call Foo1::Foo1(int i) Constructor !"<< endl; val1 = i; } private: int val1; }; class Bar { public: int x; Foo foo; Foo1 fo1; Foo fo; char *str; Bar(){}//默认构造函数 Bar(int i){x=i;} }; void foo_bar() { Bar bar; if (bar.str ) { cout << "Print the content !" << endl; } } int main() { foo_bar(); system("pause"); return 0; }

    2.2 一个类的父类自定义的无参构造函数(有non-trival的默认构造函数)

    父类有自定义的默认构造函数,子类无任何自定义构造函数

    1. #include<iostream>    
    2. using namespace std;   
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo()   
    7.   {   
    8.     cout << "Call Foo::Foo() Constructor !"<< endl;   
    9.     val = 0;   
    10.   }   
    11.   Foo(int i)   
    12.   {   
    13.     cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    14.     val = i;   
    15.    }   
    16. private:   
    17.   int val;   
    18. };   
    19. class Bar:public Foo   
    20. {   
    21. public//Foo foo;    
    22. char *str;   
    23. };   
    24. void foo_bar()   
    25. {   
    26.   Bar bar;   
    27.   if (bar.str )   
    28.   {   
    29.   cout << "Print the content !" << endl;   
    30.   }   
    31. }   
    32. int main()   
    33. {   
    34.   foo_bar();   
    35.   system("pause");   
    36.   return 0;   
    37. }  
    1. #include<iostream>   
    2. using namespace std;   
    3. class Foo   
    4. {   
    5. public:   
    6.   Foo()   
    7.   {   
    8.     cout << "Call Foo::Foo() Constructor !"<< endl;   
    9.     val = 0;   
    10.   }   
    11.   Foo(int i)   
    12.   {   
    13.     cout << "Call Foo::Foo(int i) Constructor !"<< endl;   
    14.     val = i;   
    15.    }   
    16. private:   
    17.   int val;   
    18. };   
    19. class Bar:public Foo   
    20. {   
    21. public//Foo foo;   
    22. char *str;   
    23. };   
    24. void foo_bar()   
    25. {   
    26.   Bar bar;   
    27.   if (bar.str )   
    28.   {   
    29.   cout << "Print the content !" << endl;   
    30.   }   
    31. }   
    32. int main()   
    33. {   
    34.   foo_bar();   
    35.   system("pause");   
    36.   return 0;   
    37. }  


    如果一个没有任何constructor的派生类继承自一个带有default constructor(用户定义的)的base class, 那么这个派生类的default constructor被认为是nontrivial,而对于nontrivial的default constructor, 编译器会为他合成出来。在合成出的default constructor中调用base class的default constuctor.

     如果设计者提供了多个constructor,但为提供default constuctor,那编译器不会合成新的default constructor,而是会扩展所有的现有的constructor,安插进去default constructor所必须的代码。如果此类中仍存在第一种情况,也就是说存在有menber object, 而且object含有default constructor, 那这些default constructor 也会被调用,在base class的default constructor被调用后。

    如果把//Foo foo这句也加上,结果为:

    因为在创建子类对象时需要调用父类的构造函数,同时,由于子类包含父类成员对象,初始化成员对象时也需要调用父类构造函数。

    2.3一个类里隐式的含有Virtual tabel(Vtbl)或者pointer member(vptr),并且其基类无任何构造函数或者有用户自定义的默认构造函数。

    vtbl或vptr需要编辑器隐式的合成出来,那么编辑器就把合成动作放在了默认构造函数里,所以编辑器必需自己产生一个构造函数来完成这些动作。所以你的类里只要含有virtual function,那么编辑器就会生成默认的构造函数。

    无论一个class是声明(或继承)了一个virtual function, 还是派生自一个继承串联,其中有一个或多个virtual base class.不管上述哪种情况,由于缺乏由user声明的constructor, 编译器会详细记录合成一个default constructor的详细信息。

     在编译期间,会做以下的扩张工作:

    (1) 一个virtual function table会被编译器产生出来,内含virtual functions的地址。

    (2) 编译器会合成一个vptr, 插入每一个object中。

    而合成出来的default constructor,当然会为每一个object 设定vptr的初值。但不会为类的成员变量初始化。

    例,基类有虚函数,并且无构造函数或者有用户自定义的默认构造函数:

    class Base{ public: int x; // Base(){} 有没有这句效果都是一样的 virtual void set() { cout<<"Base set"<<endl; cout<<x<<endl; } };class Derived:public Base{ public: int y; void set() { cout<<"Derived set"<<endl; cout<<y<<endl; } };void func(Base &b){ b.set(); }int main(){ Base b; Derived d; func(b); func(d); system("pause"); }

    例:基类为抽象类,且无构造函数。

    1. #include<iostream>   
    2. using namespace std;  
    3. class Widget  
    4. {  
    5. public:   
    6.     virtual void flip() = 0;  
    7. };  
    8. class Bell: public Widget  
    9. {  
    10. public:   
    11.   void flip()  
    12.   {   
    13.     cout <<"Bell." << endl;   
    14.    }  
    15. };  
    16. class Whistle: public Widget  
    17. {  
    18. public:   
    19.   void flip()  
    20.   {   
    21.    cout <<"Whistle." << endl; }  
    22.   };  
    23. void flip(Widget &widget)  
    24. {   
    25.    widget.flip();  
    26. }  
    27. void foo()  
    28. {   
    29.   Bell b;   
    30.   Whistle w;   
    31.   flip(b);   
    32.   flip(w);  
    33. }  
    34. int main()  
    35. {  
    36.  foo();   
    37.  system("pause");  
    38. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Widget  
    4. {  
    5. public:   
    6.     virtual void flip() = 0;  
    7. };  
    8. class Bell: public Widget  
    9. {  
    10. public:   
    11.   void flip()  
    12.   {   
    13.     cout <<"Bell." << endl;   
    14.    }  
    15. };  
    16. class Whistle: public Widget  
    17. {  
    18. public:   
    19.   void flip()  
    20.   {   
    21.    cout <<"Whistle." << endl; }  
    22.   };  
    23. void flip(Widget &widget)  
    24. {   
    25.    widget.flip();  
    26. }  
    27. void foo()  
    28. {   
    29.   Bell b;   
    30.   Whistle w;   
    31.   flip(b);   
    32.   flip(w);  
    33. }  
    34. int main()  
    35. {  
    36.  foo();   
    37.  system("pause");  
    38. }  

    2.4 如果一个类虚继承与其他类

    理由和2.3一样,虚基类也需要vtbl和vptr管理,那么这些管理就需要合成构造函数来实现管理,则需要生成默认的构造函数。

    例:

    1. #include<iostream>   
    2. using namespace std;  
    3. class Base  
    4. {   
    5. public:   
    6.   int x;   
    7. void set()   
    8. {   
    9.   cout<<"Base set"<<endl;   
    10.   cout<<x<<endl;   
    11. }   
    12. };  
    13. class Derived1:virtual public Base  
    14. {   
    15. public:   
    16.   int y;   
    17. void set()   
    18. {   
    19.   cout<<"Derived1 set"<<endl;   
    20.   cout<<y<<endl;   
    21. }   
    22. };  
    23. class Derived2:virtual public Base  
    24. {   
    25. public:   
    26. int z;   
    27. void set()   
    28. {   
    29. cout<<"Derived2 set"<<endl;   
    30. cout<<z<<endl;   
    31. }   
    32. };  
    33. int main()  
    34. {   
    35. Base b;   
    36. b.set();   
    37. Derived1 d1;   
    38. d1.set();   
    39. Derived2 d2;   
    40. d2.set();   
    41. system("pause");   
    42. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Base  
    4. {   
    5. public:   
    6.   int x;   
    7. void set()   
    8. {   
    9.   cout<<"Base set"<<endl;   
    10.   cout<<x<<endl;   
    11. }   
    12. };  
    13. class Derived1:virtual public Base  
    14. {   
    15. public:   
    16.   int y;   
    17. void set()   
    18. {   
    19.   cout<<"Derived1 set"<<endl;   
    20.   cout<<y<<endl;   
    21. }   
    22. };  
    23. class Derived2:virtual public Base  
    24. {   
    25. public:   
    26. int z;   
    27. void set()   
    28. {   
    29. cout<<"Derived2 set"<<endl;   
    30. cout<<z<<endl;   
    31. }   
    32. };  
    33. int main()  
    34. {   
    35. Base b;   
    36. b.set();   
    37. Derived1 d1;   
    38. d1.set();   
    39. Derived2 d2;   
    40. d2.set();   
    41. system("pause");   
    42. }  

    与如下代码效果一样:

    1. #include<iostream>   
    2. using namespace std;  
    3. class Base  
    4. {   
    5. public:   
    6. int x;   
    7. Base()//用户定义的默认构造函数 {}    
    8. Base(int i)//用用户定义的含参构造函数    
    9. { x=i; }   
    10. void set()   
    11. {   
    12. cout<<"Base set"<<endl;   
    13. cout<<x<<endl;   
    14. }   
    15. };  
    16. class Derived1:virtual public Base  
    17. {   
    18. public:   
    19. int y;  
    20. void set()   
    21. {   
    22. cout<<"Derived1 set"<<endl;   
    23. cout<<y<<endl;   
    24. }   
    25. };  
    26. class Derived2:virtual public Base  
    27. {   
    28. public:   
    29. int z;   
    30. void set()   
    31. {   
    32. cout<<"Derived2 set"<<endl;   
    33. cout<<z<<endl;   
    34. }   
    35. };  
    36. int main()  
    37. {   
    38. Base b;   
    39. b.set();   
    40. Derived1 d1;   
    41. d1.set();   
    42. Derived2 d2;   
    43. d2.set();   
    44. system("pause");   
    45. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Base  
    4. {   
    5. public:   
    6. int x;   
    7. Base()//用户定义的默认构造函数 {}   
    8. Base(int i)//用用户定义的含参构造函数   
    9. { x=i; }   
    10. void set()   
    11. {   
    12. cout<<"Base set"<<endl;   
    13. cout<<x<<endl;   
    14. }   
    15. };  
    16. class Derived1:virtual public Base  
    17. {   
    18. public:   
    19. int y;  
    20. void set()   
    21. {   
    22. cout<<"Derived1 set"<<endl;   
    23. cout<<y<<endl;   
    24. }   
    25. };  
    26. class Derived2:virtual public Base  
    27. {   
    28. public:   
    29. int z;   
    30. void set()   
    31. {   
    32. cout<<"Derived2 set"<<endl;   
    33. cout<<z<<endl;   
    34. }   
    35. };  
    36. int main()  
    37. {   
    38. Base b;   
    39. b.set();   
    40. Derived1 d1;   
    41. d1.set();   
    42. Derived2 d2;   
    43. d2.set();   
    44. system("pause");   
    45. }  

    3.当用带有默认参数的构造函数进行初始化的时候,可以用类似默认参数初始化类的对象的方式来进行初始化。

    特别注意:以下所有情况均为把有参构造函数用默认值初始化的特例(长得像,但并不是默认构造函数),并非有默认构造函数。创建对象方式与默认构造函数相同,但意义不一样。以下是在声明时成成员初始化为0,则调用形式与默认构造函数相同。其本质是使用带

    例1:父类无构造函数(有编译器自己创建的trival型的),子类已经有有参构造函数。生成对象的时候实际调用的是用参构造函数,只不过参数都是0,可以省略不写。

    1. #include<iostream>   
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. void set()//或者virtual void set()    
    9. { cout<<"Base"<<endl; }   
    10. };   
    11. class Derived:public Point   
    12. protected:   
    13. int x1,y1;   
    14. public:   
    15. Derived(int m=0,int n=0)   
    16. { x1=m; y1=n; }   
    17. void set()  
    18. {cout<<"Derived_set()"<<endl;}   
    19. void draw(){cout<<"Derived_draw()"<<endl;}   
    20. };  
    21. void test(Point *b){ b->set(); }  
    22. int main()  
    23. {   
    24. Derived *dr=new Derived;   
    25. test(dr);   
    26. Derived drr;   
    27. test(&drr);  
    28.  system("pause");   
    29. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. void set()//或者virtual void set()   
    9. { cout<<"Base"<<endl; }   
    10. };   
    11. class Derived:public Point   
    12. protected:   
    13. int x1,y1;   
    14. public:   
    15. Derived(int m=0,int n=0)   
    16. { x1=m; y1=n; }   
    17. void set()  
    18. {cout<<"Derived_set()"<<endl;}   
    19. void draw(){cout<<"Derived_draw()"<<endl;}   
    20. };  
    21. void test(Point *b){ b->set(); }  
    22. int main()  
    23. {   
    24. Derived *dr=new Derived;   
    25. test(dr);   
    26. Derived drr;   
    27. test(&drr);  
    28.  system("pause");   
    29. }  

    如果是

    virtual void set()

    则结果为:



    例2:基类含无参构造函数,子类已经有有参构造函数。

    1. #include<iostream>   
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. void set()   
    9. { cout<<"Base"<<endl;   
    10. }   
    11. Point(){//基类无参构造函数   
    12.  }  
    13. };   
    14. class Derived:public Point   
    15. {   
    16. protected:   
    17. int x1,y1;   
    18. public:   
    19. Derived(int m=0,int n=0)   
    20. { x1=m; y1=n; }   
    21. void set(){cout<<"Derived_set()"<<endl;}   
    22. void draw(){cout<<"Derived_draw()"<<endl;}   
    23. };  
    24. void test(Point *b)  
    25. {   
    26. b->set();   
    27. }  
    28. int main()  
    29. {   
    30. Derived *dr=new Derived;   
    31. test(dr);   
    32. Derived drr;   
    33. test(&drr);   
    34. system("pause");   
    35. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. void set()   
    9. { cout<<"Base"<<endl;   
    10. }   
    11. Point(){//基类无参构造函数  
    12.  }  
    13. };   
    14. class Derived:public Point   
    15. {   
    16. protected:   
    17. int x1,y1;   
    18. public:   
    19. Derived(int m=0,int n=0)   
    20. { x1=m; y1=n; }   
    21. void set(){cout<<"Derived_set()"<<endl;}   
    22. void draw(){cout<<"Derived_draw()"<<endl;}   
    23. };  
    24. void test(Point *b)  
    25. {   
    26. b->set();   
    27. }  
    28. int main()  
    29. {   
    30. Derived *dr=new Derived;   
    31. test(dr);   
    32. Derived drr;   
    33. test(&drr);   
    34. system("pause");   
    35. }  

    结果:

    例3:基类含有参构造函数

    1. #include<iostream>   
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. void set()   
    9. { cout<<"Base"<<endl; }   
    10. Point(int i,int j)  
    11. { x0=i; y0=j; }   
    12. };   
    13. class Derived:public Point   
    14. {   
    15. protectedint x1,y1;   
    16. public:   
    17. Derived(int m=0,int n=0,int i=0,int j=0):Point(i,j)   
    18. { x1=m; y1=n; }   
    19. void set()  
    20. {cout<<"Derived_set()"<<endl;}   
    21. void draw()  
    22. {cout<<"Derived_draw()"<<endl;}   
    23. };  
    24. void test(Point *b)  
    25. { b->set(); }  
    26. int main()  
    27. {   
    28. Derived *dr=new Derived;   
    29. test(dr);   
    30. Derived drr;   
    31. test(&drr);   
    32. system("pause");   
    33. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. void set()   
    9. { cout<<"Base"<<endl; }   
    10. Point(int i,int j)  
    11. { x0=i; y0=j; }   
    12. };   
    13. class Derived:public Point   
    14. {   
    15. protectedint x1,y1;   
    16. public:   
    17. Derived(int m=0,int n=0,int i=0,int j=0):Point(i,j)   
    18. { x1=m; y1=n; }   
    19. void set()  
    20. {cout<<"Derived_set()"<<endl;}   
    21. void draw()  
    22. {cout<<"Derived_draw()"<<endl;}   
    23. };  
    24. void test(Point *b)  
    25. { b->set(); }  
    26. int main()  
    27. {   
    28. Derived *dr=new Derived;   
    29. test(dr);   
    30. Derived drr;   
    31. test(&drr);   
    32. system("pause");   
    33. }  

    结果:


    例4:对于基类含有虚函数和抽象类也和上面所说的情况一致。

    1. #include<iostream>   
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. Point(int i,int j)   
    9. { x0=i; y0=j; }   
    10. virtual void set()=0;   
    11. virtual void draw()=0;   
    12. };  
    13. class Line:public Point  
    14. {   
    15. protectedint x1,y1;   
    16. public: Line(int i=0,int j=0,int m=0,int n=0):Point(i,j)   
    17. { x1=m; y1=n; }   
    18. void set()  
    19. {cout<<"Line_set()"<<endl;}   
    20. void draw(){cout<<"Line_draw()"<<endl;}   
    21. };  
    22. class Ellipse:public Point  
    23. {   
    24. protected:   
    25. int x2,y2;   
    26. public:   
    27. Ellipse(int i=0,int j=0,int p=0,int q=0):Point(i,j)   
    28. { x2=p; y2=q; }   
    29. void set()  
    30. {cout<<"Ellipse_set()"<<endl;}   
    31. void draw()  
    32. {cout<<"Ellipse_draw()"<<endl;}   
    33. };  
    34. void drawobj(Point *p)  
    35. {   
    36. p->draw();   
    37. }  
    38. void setobj(Point *p)  
    39. { p->set(); }  
    40. int main()  
    41. {   
    42. Line *li=new Line();//new Line;    
    43. drawobj(li);   
    44. setobj(li);   
    45. cout<<endl;   
    46. Ellipse *el=new Ellipse();//new Ellipse;    
    47. drawobj(el);   
    48. setobj(el);   
    49. cout<<endl;   
    50. Line *li2=new Line;   
    51. drawobj(li2);   
    52. setobj(li2);   
    53. cout<<endl;   
    54. Ellipse elp;   
    55. drawobj(&elp);   
    56. setobj(&elp);   
    57. cout<<endl;   
    58. system("pause");   
    59. }  
    1. #include<iostream>  
    2. using namespace std;  
    3. class Point  
    4. {   
    5. protected:   
    6. int x0,y0;   
    7. public:   
    8. Point(int i,int j)   
    9. { x0=i; y0=j; }   
    10. virtual void set()=0;   
    11. virtual void draw()=0;   
    12. };  
    13. class Line:public Point  
    14. {   
    15. protectedint x1,y1;   
    16. public: Line(int i=0,int j=0,int m=0,int n=0):Point(i,j)   
    17. { x1=m; y1=n; }   
    18. void set()  
    19. {cout<<"Line_set()"<<endl;}   
    20. void draw(){cout<<"Line_draw()"<<endl;}   
    21. };  
    22. class Ellipse:public Point  
    23. {   
    24. protected:   
    25. int x2,y2;   
    26. public:   
    27. Ellipse(int i=0,int j=0,int p=0,int q=0):Point(i,j)   
    28. { x2=p; y2=q; }   
    29. void set()  
    30. {cout<<"Ellipse_set()"<<endl;}   
    31. void draw()  
    32. {cout<<"Ellipse_draw()"<<endl;}   
    33. };  
    34. void drawobj(Point *p)  
    35. {   
    36. p->draw();   
    37. }  
    38. void setobj(Point *p)  
    39. { p->set(); }  
    40. int main()  
    41. {   
    42. Line *li=new Line();//new Line;   
    43. drawobj(li);   
    44. setobj(li);   
    45. cout<<endl;   
    46. Ellipse *el=new Ellipse();//new Ellipse;   
    47. drawobj(el);   
    48. setobj(el);   
    49. cout<<endl;   
    50. Line *li2=new Line;   
    51. drawobj(li2);   
    52. setobj(li2);   
    53. cout<<endl;   
    54. Ellipse elp;   
    55. drawobj(&elp);   
    56. setobj(&elp);   
    57. cout<<endl;   
    58. system("pause");   
    59. }  

    展开全文
  • C++的构造函数和默认构造函数详解

    万次阅读 多人点赞 2020-07-08 14:52:53
    C++的构造函数和默认构造函数 今天学习c++时突然感觉自己对构造函数和默认构造函数的区别有些分不清,于是查找了各大网站将资料汇总一下,供自己和其他朋友们参考。 构造函数是c++的类在构造对象时调用的函数,此...

    C++的构造函数和默认构造函数详解

    构造函数是c++面向对象中的一个重难点,于是我查找了各大网站将资料汇总一下,供自己和其他朋友们参考。

    首先我们来看看构造函数的分类:

    class Complex 
    {         
     
    private :
        double m_real;
        double m_imag;
     
    public:
     
        // 无参构造函数
        // 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空
        // 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个
        //默认构造函数,如果希望有一个这样的无参构造函数,则需要自己显式地写出来
        Complex(void)
        {
             m_real = 0.0;
             m_imag = 0.0;
        } 
             
        // 一般构造函数
        // 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提
        //是参数的个数或者类型不同(基于c++的重载函数原理)
        // 例如:你还可以写一个 Complex( int num)的构造函数出来
        // 创建对象时根据传入的参数不同调用不同的构造函数
        Complex(double real, double imag)
        {
             m_real = real;
             m_imag = imag;         
         }
         
        // 复制构造函数(拷贝构造函数)
        // 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象
        //复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
        // 若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中
        //有指针成员时,由系统默认创建该复制构造函数会存在风险
        Complex(const Complex & c)
        {
            // 将对象c中的数据成员值复制过来
            m_real = c.m_real;
            m_img  = c.m_img;
        }            
     
        // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象
        // 例如:下面将根据一个double类型的对象创建了一个Complex对象
        Complex::Complex(double r)
        {
            m_real = r;
            m_imag = 0.0;
        }
     
        // 等号运算符重载
        // 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号
        //左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
        // 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
        Complex &operator=(const Complex &rhs)
        {
            // 首先检测等号右边的是否就是左边的对象本,若是本对象本身,则直接返回
            if ( this == &rhs ) 
            {
                return *this;
            }
                 
            // 复制等号右边的成员到左边的对象中
            this->m_real = rhs.m_real;
            this->m_imag = rhs.m_imag;
                 
            // 把等号左边的对象再次传出
            // 目的是为了支持连等 eg:    a=b=c 系统首先运行 b=c
            // 然后运行 a= ( b=c的返回值,这里应该是复制c值后的b对象)    
            return *this;
        }
    };
    
    void main()
    {
        // 调用了无参构造函数,数据成员初值被赋为0.0
        Complex c1,c2;
     
        // 调用一般构造函数,数据成员初值被赋为指定值
        Complex c3(1.0,2.5);
        // 也可以使用下面的形式
        Complex c3 = Complex(1.0,2.5);
             
        // 把c3的数据成员的值赋值给c1
        // 由于c1已经事先被创建,故此处不会调用任何构造函数
        // 只会调用 = 号运算符重载函数
        c1 = c3;
             
        // 调用类型转换构造函数
        // 系统首先调用类型转换构造函数,将5.2创建为一个本类的
        //临时对象,然后调用等号运算符重载,将该临时对象赋值给c1
        c2 = 5.2;
           
        // 调用拷贝构造函数( 有下面两种调用方式) 
        Complex c5(c2);
        Complex c4 = c2;  // 注意和 = 运算符重载区分,这里等号左边的对象不
                                      //是事先已经创建的,故需要调用拷贝构造函数,参数为c2       
       
    }
    

    下面再从定义出发,看看构造函数和默认构造函数的区别:

    构造函数是c++的类在构造对象时调用的函数,此函数没有返回类型。
    默认构造函数是未提供显式初始值时用来构建对象的构造函数。

    其实这个定义并没有告诉我们什么详细的细节,真正想要搞懂还是要看看代码,从代码的区别来体会他们两个之间的差异。

    class A
    {
    private:
        int  _a;
        int _b;
    public:
        A();                    // 默认构造函数
        A(int a, int b);        // 构造函数 
        A(int a = 10, int b = 2);    // 默认构造函数
    };
    

    其实如果我们没有提供任何构造函数,系统会自己帮我们加上一个默认构造函数,就像A() {};一样。当然,如你所见这个函数是空的,他不能做任何事,但是他却并不是一无是处,他的作用就是保证程序能够正确运行。

    如果我们要自己定义一个默认构造函数,那我们有两种方法:

    1.定义一个无参的构造函数(例如上面的A(); )
    2.定义所有参数都有默认值的构造函数 (例如上面的 A(int a = 10, int b = 2); )

    重点来了!切记一个类只能有一个默认构造函数!也就是说上面提到的两个方法你只能选其中的一种,不过我们大多数情况下还是选择第一种。

    另外,如果我们已经定义了构造函数,则系统不会再给我们加上默认构造函数了,这也就要求我们最好自己将默认构造函数写上,防止出现未初始化的错误。

    2019年4月7日

    展开全文
  • 一、构造函数 1.解释:构造函数也就是构造方法 2.特点: (1)构造方法的方法名必须与类名一致 (2)构造方法没有返回值类型,可以有return,但是不能有返回值,在这里只是表示结束,并不是返回的表示,一般也不...
  • 默认构造函数

    2020-01-22 15:11:25
    main.cpp int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Student *s = new Student(); s->setValues(12, "James"); s->print(); Student s2; s2 = *s;... ...
  • 【C++】默认构造函数

    千次阅读 2020-02-11 00:01:05
    默认构造函数(default constructor):类通过一个特殊的构造函数来控制默认初始化过程,这个函数称为默认构造函数默认构造函数无须任何实参。 合成的默认构造函数 如果我们的类没有显示地定义构造函数,那么...
  • 1、默认构造函数和构造函数 (1)构造函数:C++用于构建类的新对象时需要调用的函数,该函数无返回类型!(注意:是“无”! 不是空!(void))。 (2)默认构造函数默认构造函数是在调用时不需要显示地传入实参...
  • 默认构造函数和合成默认构造函数

    千次阅读 2016-03-29 19:29:29
    当我们没有为类中的对象提供初始值,此时就会执行默认初始化,类会通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做默认构造函数,这个函数并不需要任何的实参,但是如果我们的类没有显式地定义构造函数,...
  • 默认构造函数和构造函数重载

    万次阅读 多人点赞 2017-10-03 15:40:00
    本文主要总结了默认构造函数的相关用法和构造函数重载,旨在能够对平时的项目开发起到一定的夯实基本功的作用,言简意赅,一目了然。首先需要了解构造函数是用来做什么?该类对象被创建时,编译器为对象分配内存空间...
  • C++ 默认构造函数

    2018-04-27 08:45:27
    任何一个类如果没有定义默认构造函数,编译器会自动合成一个默认构造函数。2.编译器合成出来的默认构造函数,会显式的设定类中成员变量的值。先看一个最基本的例子:从图中可以看到对象a并没有被初始化,即编译器...
  • C++默认构造函数——深入理解

    万次阅读 多人点赞 2012-09-17 11:23:08
    错误认识1:若程序员没有自己定义无参数的构造函数,那么编译器会自动生成默认构造函数,来进行对成员函数的初始化。 错误认识2:编译器合成出来的default constructor会明确设定'“class内每一个data member的...
  • 而构造函数又分为默认构造函数、拷贝构造函数和自定义的构造函数。 构造函数 1、构造函数必须与类同名。 2、C++允许构造函数重载。 3、构造函数没有返回值。 默认构造函数 不带参数的 构造函数 就是 默认构造...
  • 前言 我们知道在创建对象的时候,一般会通过构造函数来进行初始化。...构造函数与默认构造函数 构造函数 构造函数,主要是用来在创建对象时初始化对象,一般会跟new运算符一起使用,给对象成员变量赋...
  • 1、如果基类定义了不带参数的默认构造函数,则编译器为派生类自动生成的默认构造函数会调用基类的默认构造函数。 2、如果基类定义了拷贝构造函数,则编译器为派生类自动生成的拷贝构造函数同样会调用基类的拷贝...
  • C++ 默认构造函数的重要性

    千次阅读 2016-06-12 16:12:13
    1、默认构造函数 C++ 默认构造函数是对类中的参数提供默认值的构造函数,一般情况下,是一个没有参数值的空函数,也可以提供一些的默认值的构造函数,如果用户没有定义构造函数,那么编译器会给类提供一个默认的...
  •   根据C++标准定义,如果程序已定义构造函数,默认情况下编译器就不再隐含生成默认构造函数。注意,这里的“构造函数”包括复制构造函数(因为复制构造函数也是构造函数的一种)。   此时该类一共有2个构造函数...
  • 1默认构造函数 当一个类没有任何构造函数时,编译器将会合成一个默认构造函数。 那么编译器合成的默认构造函数是做什么用的呢?是初始化类的成员变量吗? 事实上不是。编译器合成的默认构造函数只是满足编译器的...
  • 一、 类成员中有成员是类对象,并且该成员的类含有默认构造函数,那么C++编译器会帮你给这个类也生成一个默认构造函数,用来调用其成员对象的构造函数,完成该成员的初始化构造。需要强调的是,如果这个成员的类也...
  • 类的默认构造函数

    千次阅读 2012-05-13 16:12:42
    如果没有为一个类显式地定义任何构造函数,编译器将自动为这个类生成默认构造函数。 由编译器创建的默认构造函数称为合成的默认构造函数,它将依据如下规则初始化类中的成员变量: 1.内置数据类型的初始化 内置...
1 2 3 4 5 ... 20
收藏数 623,531
精华内容 249,412
关键字:

默认构造函数