C++在已经有构造函数时,运用默认构造函数
1.无关紧要(trivial)的默认构造函数【无用构造函数】
《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)的默认构造函数,生成的默认构造函数什么也不错,既不会讲其成员变量置零,也不会做其他的任何事情,只是为了保证程序能够正确运行而已,这就是所谓的“需要”,如果还需要给初始化成员变量,这件事情还是交给程序员做吧!
2.非平凡(non-trivival)默认构造函数【有用构造函数】
C++标准描述了哪些情况,这样的隐式默认构造函数是无关紧要的。一个非平凡(non-trivival)的默认构造函数是ARM中所说的被实现所“需要”,并在必要的时候被编译器自动生成。下面来看看默认构造函数是非平凡的四种情况:
2.1含有包含默认构造函数的成员类对象
如果该类包含一个成员类对象,它有默认的构造函数,那么这个类的隐式构造函数是非平凡的,并且编译器需要为包含的这个成员类对象生成一个默认构造函数。然后,这个编译器生成的默认构造函数只有在实际上被调用时才会被真正的生成。如下例中编译器为Bar类生成一个默认构造函数。
如果一个class中含有成员对象,而且这个对象有default constructor(如Foo foo,Foo类中有default constructor), 那么编译器就会给这个class(Bar)合成一个default constructor, 但是这个合成动作只有在调用需要时才会产生。也就是说,在需要时才会合成。
#include<iostream> using namespace std; class Foo { public: Foo(); Foo( int ); private: int val; }; Foo::Foo() { cout << "Call Foo::Foo() Constructor !"<< endl; val = 0; } Foo::Foo(int i) { cout << "Call Foo::Foo(int i) Constructor !"<< endl; val = i; } class Bar { public: Foo foo; char *str; }; void foo_bar() { Bar bar; // Bar::foo must be initialized here if (bar.str ) { cout << "Print the content !" << endl; } } int main() { foo_bar(); system("pause"); return 0; }
在这个程序片段中Bar的成员foo含有默认构造函数,它初始化自己的类成员_i为1而Bar本身并没有定义默认的构造函数,这个构造函数的目的是为了初始化它的成员变量foo,实际上就是调用Bar::foo的默认构造函数,但它并不会做一丁点关于另外一个变量str的初始化和赋值工作,初始化Bar::foo是编译器的责任,二初始化str是程序员的责任。
我们可以用以下代码来大致描述一下编译器的工作:
inline Bar::Bar() { // Pseudo C++ Code foo.Foo::Foo(); }结论:如果class中内含一个以上的含有default constructor的object,那在为class合成的default constructor中,会按照object的声明次序调用object 的 default constructor。
如果这里的Bar含有默认构造函数呢?我们可以从编译器和程序员的责任划分来考虑这个问题:
1. Bar的默认构造函数调用foo的默认构造函数,那么编译器啥也不用做了。
2. Bar的默认构造函数中没有去foo这个成员变量,那么编译器需要去帮助程序员把这件事情做完,插入一条类似“foo.Foo::Foo();”的代码。
注:如果Bar含有多个成员类变量,则编译器会按照这些变量的声明顺序去做以上处理。
对比,以下代码不符合上述要求,会报错:#include<iostream> using namespace std; class Foo { public: Foo(); //去掉默认构造函数会报错 Foo( int ); private: int val; }; Foo::Foo() { cout << "Call Foo::Foo() Constructor !"<< endl; val = 0; } Foo::Foo(int i) { cout << "Call Foo::Foo(int i) Constructor !"<< endl; val = i; } class Bar { public: Foo foo(1); //也会报错。expected `;' before '(' token char *str; }; void foo_bar() { Bar bar; // Bar::foo must be initialized here if (bar.str ) { cout << "Print the content !" << endl; } } int main() { foo_bar(); system("pause"); return 0; }
2.2 一个类有父类,则编译器会为这个继承类产生一个默认构造函数。
对父类无限制。以下情况都可以:
2.2.1父类无构造函数
父类无构造函数(有编译器自己创建的trival型的),子类无构造函数。
#include<iostream> using namespace std; class Point { protected: int x0,y0; public: void set() { cout<<"Base"<<endl; } }; class Derived:public Point { public: void set() { cout<<"Derived"<<endl; } }; void test(Point *b) { b->set(); } int main() { Derived *dr=new Derived; test(dr); Derived drr; test(&drr); system("pause"); }
===========================================以上为编译器生成(有用)默认构造函数======================================================
特别注意:以下所有情况均为把有参构造函数使用成无参构造函数,创建对象方式与默认构造函数相同,但意义不一样。以下是在声明时成成员初始化为0,则调用函数形式与默认构造函数相同。
父类无构造函数(有编译器自己创建的trival型的),子类已经有有参构造函数。
#include<iostream> using namespace std; class Point { protected: int x0,y0; public: void set()//或者virtual void set() { cout<<"Base"<<endl; } }; class Derived:public Point { protected: int x1,y1; public: Derived(int m=0,int n=0) { x1=m; y1=n; } void set(){cout<<"Derived_set()"<<endl;} void draw(){cout<<"Derived_draw()"<<endl;} }; void test(Point *b) { b->set(); } int main() { Derived *dr=new Derived; test(dr); Derived drr; test(&drr); system("pause"); }
如果是
virtual void set()则结果为:
2.2.2父类含有用户自定义的无参构造函数或者有参构造函数。
基类含无参构造函数,子类已经有有参构造函数,也能为子类生成默认构造函数。
#include<iostream> using namespace std; class Point { protected: int x0,y0; public: void set() { cout<<"Base"<<endl; } Point(){//基类无参构造函数 } }; class Derived:public Point { protected: int x1,y1; public: Derived(int m=0,int n=0) { x1=m; y1=n; } void set(){cout<<"Derived_set()"<<endl;} void draw(){cout<<"Derived_draw()"<<endl;} }; void test(Point *b) { b->set(); } int main() { Derived *dr=new Derived; test(dr); Derived drr; test(&drr); system("pause"); }结果:
基类含有参构造函数
#include<iostream> using namespace std; class Point { protected: int x0,y0; public: void set() { cout<<"Base"<<endl; } Point(int i,int j){ x0=i; y0=j; } }; class Derived:public Point { protected: int x1,y1; public: Derived(int m=0,int n=0,int i=0,int j=0):Point(i,j) { x1=m; y1=n; } void set(){cout<<"Derived_set()"<<endl;} void draw(){cout<<"Derived_draw()"<<endl;} }; void test(Point *b) { b->set(); } int main() { Derived *dr=new Derived; test(dr); Derived drr; test(&drr); system("pause"); }结果:
对于基类含有虚函数和抽象类也和上面所说的情况一致。例:
#include<iostream> using namespace std; class Point { protected: int x0,y0; public: Point(int i,int j) { x0=i; y0=j; } virtual void set()=0; virtual void draw()=0; }; class Line:public Point { protected: int x1,y1; public: Line(int i=0,int j=0,int m=0,int n=0):Point(i,j) { x1=m; y1=n; } void set(){cout<<"Line_set()"<<endl;} void draw(){cout<<"Line_draw()"<<endl;} }; class Ellipse:public Point { protected: int x2,y2; public: Ellipse(int i=0,int j=0,int p=0,int q=0):Point(i,j) { x2=p; y2=q; } void set(){cout<<"Ellipse_set()"<<endl;} void draw(){cout<<"Ellipse_draw()"<<endl;} }; void drawobj(Point *p) { p->draw(); } void setobj(Point *p) { p->set(); } int main() { Line *li=new Line();//new Line; drawobj(li); setobj(li); cout<<endl; Ellipse *el=new Ellipse();//new Ellipse; drawobj(el); setobj(el); cout<<endl; Line *li2=new Line; drawobj(li2); setobj(li2); cout<<endl; Ellipse elp; drawobj(&elp); setobj(&elp); cout<<endl; system("pause"); }