3.6 C++继承机制下的构造函数
参考:http://www.weixueyuan.net/view/6363.html
总结:
在codingbook类中新增了一个language成员变量,为此必须重新设计新的构造函数。在本例中book类中有一个默认构造函数和一个带参数的构造函数,codingbook类中同样声明了两个构造函数,一个默认构造函数和一个带参数的构造函数,默认构造函数显式调用基类的默认构造函数,带参构造函数显式调用基类的带参构造函数。
codingbook():book(){lang = none;} //定义派生类的构造函数时 显示调用 基类的构造函数
codingbook::codingbook(language lang, char * t, double p):book(t,p)
如果继承关系有好几层的话,例如A类派生出B类,B类派生出C类,则创建C类对象时,构造函数的执行顺序则为A的构造函数,其次是B的构造函数,最后是C类的构造函数。构造函数的调用顺序是按照继承的层次,自顶向下,从基类再到派生类的。
在前一章节中,我们介绍了构造函数的功能和用法,派生类同样有构造函数。当我们创建一个派生类对象的时候,基类构造函数将会被自动调用,用于初始化派生类从基类中继承过来的成员变量。而派生类中新增的成员变量则需要重新定义构造函数用于初始化了。
例1:
#include<iostream> using namespace std; class book { public: book(); book(char* a, double p = 5.0); void setprice(double a); double getprice()const; void settitle(char* a); char * gettitle()const; void display(); private: double price; char * title; }; class book_derived :public book { public: void display(); }; book::book(char* a, double p) { title = a; price = p; } book::book() { title = "NoTitle"; price = 0.0; } void book::setprice(double a) { price = a; } double book::getprice()const { return price; } void book::settitle(char* a) { title = a; } char * book::gettitle()const { return title; } void book::display() { cout<<"The price of "<<title<<" is $"<<price<<endl; } void book_derived::display() { cout<<"The price of "<<gettitle()<<" is $"<<getprice()<<endl; } int main() { book_derived b; b.display(); return 0; }
在本例中定义了book_derived类,该类没有自身的成员变量,类中所有成员变量都继承自book类,类中成员函数仅有一个display函数,该函数遮蔽了基类book中的display函数。在主函数中定义派生类的对象b,之后调用派生类的display函数,程序运行结果为:“The price of NoTitle is $0”。
从这例1中,我们不难看出派生类在创建对象时会自动调用基类构造函数。如果像例1这种情况,派生类中没有新增成员变量,基类的构造函数功能已经满足派生类创建对象初始化需要,则派生类则无需重新自定义一个构造函数,直接调用基类构造函数即可。如果派生类中新增了成员变量,这时如果需要在创建对象时就进行初始化则需要自己设计一个构造函数,具体见例2。
例2:
#include<iostream> using namespace std; enum language{none, cpp, java, python, javascript, php, ruby}; class book { public: book(); book(char* a, double p = 5.0); void setprice(double a); double getprice()const; void settitle(char* a); char * gettitle()const; void display(); private: double price; char * title; }; class codingbook: public book { public : codingbook():book(){lang = none;} codingbook(language lang, char * t, double p); void setlang(language lang); language getlang(){return lang;} void display(); private: language lang; }; book::book(char* a, double p) { title = a; price = p; } book::book() { title = "NoTitle"; price = 0.0; } void book::setprice(double a) { price = a; } double book::getprice()const { return price; } void book::settitle(char* a) { title = a; } char * book::gettitle()const { return title; } void book::display() { cout<<"The price of "<<title<<" is $"<<price<<endl; } void codingbook::setlang(language lang) { this->lang = lang; } codingbook::codingbook(language lang, char * t, double p):book(t,p) { this->lang = lang; } void codingbook::display() { book::display(); cout<<"The language is "<<lang<<endl; } int main() { codingbook cpp; cpp.display(); codingbook java(java, "Thinking in Java", 59.9); java.display(); return 0; }
本例中定义了两个类book类和codingbook类,codingbook类是book类的派生类。在codingbook类中新增了一个language成员变量,为此必须重新设计新的构造函数。在本例中book类中有一个默认构造函数和一个带参数的构造函数,codingbook类中同样声明了两个构造函数,一个默认构造函数和一个带参数的构造函数,默认构造函数显式调用基类的默认构造函数,带参构造函数显式调用基类的带参构造函数。在主函数中定义了codingbook类的对象cpp,该对象调用codingbook类的默认构造函数,codingbook类中的默认构造函数先会调用基类的默认构造函数将title和price进行初始化,之后才会执行自身函数体中的内容。之后又定义了codingbook类对象java,该对象在定义时后面接有三个参数,很明显是需要调用codingbook类的带参构造函数,其中java参数用于初始化lang成员变量,而后两个参数则用于初始化从基类继承过来的title和price两个成员变量,当然初始化顺序依然是先调用基类的带参构造函数初始化title和price,然后再执行自身函数体中的初始化代码初始化lang成员变量。
最后程序运行结果如下:
The price of NoTitle is $0
The language is 0
The price of Thinking in Java is $59.9
The language is 2
在这个例子中language没有显示为java或者cpp,只显示为0和2,这个熟悉枚举类型的应该都清楚,枚举类型在本例中其实就是从0开始的int类型。
从例2中,我们可以很清楚的看到,当我们创建派生类对象时,先由派生类构造函数调用基类构造函数,然后再执行派生类构造函数函数体中的内容,也就是说先执行基类构造函数,然后再去执行派生类构造函数。如果继承关系有好几层的话,例如A类派生出B类,B类派生出C类,则创建C类对象时,构造函数的执行顺序则为A的构造函数,其次是B的构造函数,最后是C类的构造函数。构造函数的调用顺序是按照继承的层次,自顶向下,从基类再到派生类的。
例3:
#include<iostream> using namespace std; class base { public: base(){x = 0; y = 0; cout<<"base default constructor"<<endl;} base(int a, int b){x = a; y = b; cout<<"base constructor"<<endl;} private: int x; int y; }; class derived: public base { public: derived():base(){z = 0; cout<<"derived default constructor"<<endl;} derived(int a, int b, int c):base(a,b){z = c; cout<<"derived constructor"<<endl;} private: int z; }; int main() { derived A; derived B(1,2,3); return 0; }
本例中定义了两个类,基类base中定义了一个默认构造函数和一个带参数的构造函数。派生类derived中同样定义了两个构造函数,这两个构造函数一个为默认构造函数,一个为带参构造函数。派生类中的默认构造函数显式调用基类默认构造函数,带参构造函数显式调用基类的带参构造函数。我们在主函数中定义了派生类的两个对象,这两个对象一个是调用派生类的默认构造函数,另一个调用派生类的带参构造函数。
这个程序运行结果如下:
base default constructor
derived default constructor
base constructor
derived constructor
从运行结果可以看出创建对象时先是执行基类的构造函数,然后再是执行拍摄呢类构造函数。构造函数执行顺序是按照继承顺序自顶向下执行。