类的继承与派生中关于构造函数

来源:互联网 发布:哥白尼式革命 知乎 编辑:程序博客网 时间:2024/05/21 18:34

单继承:一个派生类只从一个基类派生。

多重继承:一个派生类有两个和多个基类。

         在目前的C++标准中,派生类把基类全部的成员(不包括构造函数和析构函数)接收过来,也就是说是没有选择的,不能说选择接受一部分成员,而舍弃另一部分成员。

此外,可以在派生类中声明一个与基类成员同名的成员,但应注意:如果是成员函数,不仅应使函数名相同,而且函数的参数列表(参数的个数和类型)也应相同,如何不相同,就成为函数的重载而不是覆盖了。

#include<iostream>#include<string>using namespace std;class student{public:student(int n,string nam,char s):num(n),name(nam),sex(s){}~student(){}protected:int num;string name;char sex;};class student1:public student{public:student1(int n,string nam,char s,int a,string ad):student(n,nam,s),age(a),addr(ad){}void show(){cout<<"num:"<<num<<endl;cout<<"name:"<<name<<endl;cout<<"sex:"<<sex<<endl;cout<<"age:"<<age<<endl;cout<<"address:"<<addr<<endl; }~student1(){}private:int age;string addr;};int main(){student1 stu1(10010,"wang",'f',19,"beijing");stu1.show();return 0;}


       为什么声明protected而不是private,因为派生类要用到基类的数据成员。

派生类构造函数的一般形式为:

       派生类构造函数名(总参数表):基类构造函数名(参数表){派生类中新增数据成员初始化语句}。

注意,这里是调用基类构造函数而不是定义基类构造函数,因此这些参数是实参而不是形参,所以基类构造函数名后面的参数表不能指定参数类型,只需传递参数即可。

执行构造函数的顺序:

1:派生类构造函数先调用基类构造函数

2:再执行派生类构造函数本身,(即派生类构造函数的函数体)。

对上例来说,先初始化num,name,sex,然后再初始化age,和addr。


#include<iostream>#include<string>using namespace std;class Student{public:Student(int n,string nam):num(n),name(nam){}void display(){cout<<"num:"<<num<<endl<<"name:"<<name<<endl;}protected:int num;string name;} ;class Student1:public Student{public:Student1(int n,string nam,int n1,string nam1,int a,string ad):Student(n,nam),monitor(n1,nam1){age=a;addr=ad;}void show(){cout<<"This student is:";display();cout<<"age:"<<age<<endl;cout<<"address"<<addr<<endl;}void show_monitor(){cout<<endl<<"Class monitor is:"<<endl;monitor.display();}private:Student monitor;int age;string addr;};int main(){Student1 stud1(1001,"wang",1000,"Li",19,"beijing");stud1.show();stud1.show_monitor();return 0;}

       子对象的初始化不是在声明派生类时进行的,因为类是抽象类型,只是一个模型,不占储存空间,无法初始化。因此子对象的初始化是在建立派生类时通过调用派生类构造函数来实现的。

1:对基类数据成员初始化;

2:对子对象数据成员初始化;

3:对派生类数据成员初始化;

定义派生类构造函数的一般形式为:

       派生类构造函数名(总参数表):基类构造函数名(参数表),子对象(参数表){派生类中新增数据成员初始化语句}。

执行派生类构造函数的顺序是:

1:调用基类构造函数,对基类数据成员初始化;

2:调用子对象构造函数,对子对象数据成员初始化;

3:再执行派生类构造函数本身,对派生类数据成员初始化;

       对上例来说,先初始化基类中的数据成员num,name,然后再初始化子对象的数据成员num,name,最后初始化派生类中的数据成员age和addr。

这里所说基类中的数据成员,子对象的数据成员,派生类中的数据成员,可能有点歧义,意思不是成员在基类中占存储空间。成员肯定是Student1的对象stu1中的数据成员,而前面表述的意思是从基类继承的成员的意思。

       在这里我有一个疑问,既然已经将基类成员继承过来了,为什么不直接用派生类构造函数一起初始化,而非要调用基类的构造函数呢?

       在这里说下我的理解,如果基类有构造函数对数据进行初始化,子类继承父类,虽然无法继承他的构造函数,但是从理论上来说,是应该原封不动继承过来的(虚函数除外),如果你自己定义一个构造函数,鬼知道你初始化的方法是不是和你父类一样,要是不一样,不就违背了继承的定义了吗?所以C++只允许调用父类的构造函数,以父类的方法初始化父类的成员。

       如果在基类中既定义了无参构造函数,又定义了有参构造函数(构造函数重载),则在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构造函数。在调用派生类构造函数时,根据构造函数的内容决定调用基类的有参构造函数还是无参构造函数。

虚基类的初始化

贴代码:

#include<iostream>#include<string>using namespace std;class Person{public:Person(string nam,char s,int a){name=nam;sex=s;age=a;} protected:string name;char sex;int age;};class Teacher:virtual public Person{public:Teacher(string nam,char s,int a,string t):Person(nam,s,a),title(t){}protected:string title;};class Student:virtual public Person{public:Student(string nam,char s,int a,float sco):Person(nam,s,a){score=sco;}protected:float score;};class Graduate:public Teacher,public Student{public:Graduate(string nam,char s,int a,string t,float sco,float w):Person(nam,s,a),Teacher(nam,s,a,t),Student(nam,s,a,sco){wage=w;}void show(){cout<<"name:"<<name<<endl;cout<<"age:"<<age<<endl;cout<<"sex:"<<sex<<endl;cout<<"score:"<<score<<endl;cout<<"title:"<<title<<endl;cout<<"wages:"<<wage<<endl;}private:float wage;};int main(){Graduate grad1("www.",'f',24,"assistant",89.5,1200);grad1.show();return 0;}

一般情况下都是对直接基进行初始化,然后由直接基负责对直接基的直接基初始化。

对于虚基类的初始化,因为在派生类中只保留一份文件,所以如果对两个或多个直接基初始化时初始化的值不一样,当这些直接基对虚基类初始化时,可能会不同,所以最后的派生类要负责对其直接基类进行初始化,以及虚基类的初始化。

在这里要强调:C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(上面的Person和Student)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。

1 0
原创粉丝点击