C++
继承与派生
1.公有继承
是指在源生一个类时继承方式为public的继承方式。在public继承方式下,基类成员在派生类中的访问权限为:基类的公有和保护成员的访问属性在派生类中不变而基类的私有成员不可访问。即基类的公有成员和保护成员被继承到派生类中仍作为派生类的公有成员和保护成员。派生类的其他成员可以直接访问它们。但需要注意的是,这与派生类的对象对基类成员的边访问权限是两个不同概念。派生类的对象是在类外定义的,派生类的对象可以访问基类的公有成员。
#include <iostream>
using namespace std;
class A
{
public:
int a1;
protected:
int a2;
private:
int a3;
public:
A() //类A的构造函数,在public里
{
a1=1;a2=2;a3=3;
}
void print1() //成员函数
{
cout<<"a1="<<a1<<", a2="<<a2<<", a3="<<a3<<endl;
}
};
class B1:public A //派生类B1公有继承基类A
{
public:
int b1; //B1新增公有成员
B1(int cb1) //类B1的构造函数
{
b1=cb1;
}
void print2()
{
cout<<"a1="<<a1; //派生类成员函数访问基类公有成员
cout<<", a2="<<a2<<endl; //派生类成员函数访问基类保护成员
//cout<<"a3="<<a3<<endl; /派生类成员函数不能访问基类私有成员
cout<<"b1="<<b1<<endl;
}
};
int main()
{
B1 ob1(100); //定义派生类的对象ob1
cout<<"ob1.a1="<<ob1.a1<<endl; //派生类的对象ob1可以访问基类公有成员
//cout<<"ob1.a2="<<ob1.a2<<endl;ob1是派生类的对象,不能访问基类保护成员a2和私有成员a3
//cout<<"ob1.a3="<<ob1.a3<<endl;
ob1.print1();
ob1.print2();
return(0);
}
运行结果:
ob1.a1=1
a1=1, a2=2, a3=3
a1=1, a2=2
b1=100
2.私有继承
私有继承是指在派生一个类时继承方式为private 的继承方式。基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可访问。即基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类外部通过派生类的对象无法访问。
#include <iostream>
using namespace std;
class A
{
public:
int a1;
protected:
int a2;
private:
int a3;
public:
A() //类A的构造函数,在public里
{
a1=1;a2=2;a3=3;
}
void print1() //成员函数
{
cout<<"a1="<<a1<<", a2="<<a2<<", a3="<<a3<<endl;
}
};
class B1:private A //派生类B1私有继承基类A
{
public:
int b1; //B1新增公有成员
B1 (int cb1) //类B1的构造函数
{
b1=cb1;
}
void print2()
{
cout<<"a1="<<a1; //派生类成员函数访问基类公有成员
cout<<", a2="<<a2<<endl; //派生类成员函数访问基类保护成员
//cout<<"a3="<<a3<<endl; /派生类成员函数不能访问基类私有成员
cout<<"b1="<<b1<<endl;
}
};
int main()
{
B1 ob1(100); //定义派生类的对象ob1
//cout<<"ob1.a1="<<ob1.a1<<endl; //派生类的对象ob1不可以访问基类公有成员
//cout<<"ob1.a2="<<ob1.a2<<endl;ob1是派生类的对象,不能访问基类保护成员a2和私有成员a3
//cout<<"ob1.a3="<<ob1.a3<<endl;
//b1.print1(); //生类的对象ob1不可以访问基类公有成员函数
ob1.print2();
return(0);
}
运行结果:
a1=1, a2=2
b1=100
该例中无论是派生类的成员函数print2()还是通过派生类的对象ob1,都无法访问基类的私有成员a3。所以如果要输出a3的值,可以在类B1中增加一个成员函数,如:void print3(){print1();}
这样,在主函数中可以通过ob1.print3()间接访问a3。
如果继续派生,那么该基类的所有成员对于新的派生类来说都是不可访问的,即以privare 方式的继承,只能“传递一代”,基类成员无法在进一 步的派生中发挥作用。例如B1继续派生一个新类 B11时,在B11中无法访问 B1从A继承的任何成员了。
3.保护继承
保护继承是指在派生一个类时继承方式为protected的继承方式。保护继承方式的访问控制权限介于公有继承和私有继承之间。保护继承的特点是基类的所有公有成和保护成员都成为派生类的保护成员,基类的private成员和不可访问成员在派生类中可访问。
派生类的成员可以直接访问基类的public成员和protected成员,而派生类的对象无直接访问基类的任何成员,这跟私有继承的效果一样。如果把程序中的private为protected,则派生类由私有继承改为了保护继承,程序运行结果完全一样。但是,如果从派生类再往下派生新的类时,保护继承和私有继承就有区别了。例如:
(1)类B以protected方式继承类A,不管类C以何种方式继承类B,那么类C的成员函数可以访问间接基类类A的public或protected成员。
(2)类B以pivate 方式继承类A,不管类C以何种方式继承类B,那么类C的成员函数不能直接访问类A的所有成员。因为类A的成员已经变成类B的pireate成员,类B再派生类C,类B的私有成员对于类C的成员自然是不可见的。
#include <iostream>
using namespace std;
class A
{
public:
int a1;
protected:
int a2;
private:
int a3;
public:
A() //类A的构造函数,在public里
{
a1=1;a2=2;a3=3;
}
void print1() //成员函数
{
cout<<"a1="<<a1<<", a2="<<a2<<", a3="<<a3<<endl;
}
};
class B1:protected A //派生类B1私有继承基类A
{
public:
int b1; //B1新增公有成员
B1 (int cb1) //类B1的构造函数
{
b1=cb1;
}
void print2()
{
cout<<"a1="<<a1; //派生类成员函数访问基类公有成员
cout<<", a2="<<a2<<endl; //派生类成员函数访问基类保护成员
//cout<<"a3="<<a3<<endl; /派生类成员函数不能访问基类私有成员
cout<<"b1="<<b1<<endl;
}
void print3()
{
print1();
}
};
int main()
{
B1 ob1(100); //定义派生类的对象ob1
//cout<<"ob1.a1="<<ob1.a1<<endl; //派生类的对象ob1不可以访问基类公有成员
//cout<<"ob1.a2="<<ob1.a2<<endl;ob1是派生类的对象,不能访问基类保护成员a2和私有成员a3
//cout<<"ob1.a3="<<ob1.a3<<endl;
ob1.print2(); //生类的对象ob1不可以访问基类公有成员函数
ob1.print3();
return(0);
}
运行结果:
a1=1, a2=2 , a3=3
b1=100
a1=1, a2=2 , a3=3
派生类的构造函数和析构函数
派生类的构造函数
在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还要考虑基类数据成员的初始化。
(1)简单派生类的构造函数
派生类构造函数名(总参数列表):基类构造函数名(参数列表)
{派生类中新增数据成员初始化语句;}
执行顺序:派生类构造函数先调用基类构造函数,再执行派生类构造函数本身。
#include <iostream>
using namespace std;
class A
{
public:
A(int i)
{
x=i;
cout<<"基类A的构造函数被调用"<<endl;
}
void display1()
{
cout<<"基类私有数据成员x="<<x<<endl;
}
private:
int x;
};
class B:public A
{
public:
B(int i,int j):A(i) //派生类B的构造函数要以参数列表方式为基类A传递参数,用于x的初始化
{
y=j;
cout<<"派生类B的构造函数被调用"<<endl;
}
void display2()
{
display1();
cout<<"派生类私有数据成员y="<<y<<endl;
}
private:
int y;
};
int main()
{
B b(1,2); //对象b必须要有两个参数
b.display2();
}
运行结果:
基类A的构造函数被调用
派生类B的构造函数被调用
基类私有数据成员x=1
派生类私有数据成员y=2
派生类B的构造函数应该以参数列表方式给基类A传递参数,用来进行基类成员的初始化。
(2)派生类中包含内嵌对象
派生类构造函数名(总参数列表):基类构造函数名(参数列表),内嵌对象名(内嵌对象参数)
{派生类中新增数据成员初始化语句;}
执行顺序:派生类构造函数先调用基类构造函数,然后是内嵌对象的构造函数,最后执行派生类构造函数本身。
#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;
cout<<"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<<"该学生是:"<<endl;
display();
cout<<"age:"<<age<<endl;
cout<<"address:"<<addr<<endl;
}
void show_monitor() //输出子对象的数据成员
{
cout<<endl<<"班长是:"<<endl;
monitor.display(); //调用基类成员函数
}
private:
Student monitor; //定义子对象
int age;
string addr;
};
int main()
{
Student1 stud1(10010,"wang-li",10001,"li-sun",19,"115 Beijing Road,Shanghai");
stud1.show(); //输出第一个学生的数据
stud1.show_monitor(); //输出子对象的数据
return(0);
}
运行结果:
该学生是:
num:10010
name:wang-li
age:19
address:115 Beijing Road,Shanghai
班长是:
num:10001
name:li-sun
多继承
多继承的定义格式:
class 派生类名:继承方式1 基类名1,继承方式2 基类名2,·······
{新增的数据成员和函数成员的描述;}
多继承时,派生类的构造函数格式:
派生类名(总参数列表):基类名1(参数列表1),基类名2(参数列表2),······,基类名n(参数列表n),子对象名1(参数表1),子对象名2(参数表2),······,子对象名n(参数表n)
{派生类构造函数体;}
#include <iostream>
using namespace std;
class B1
{
public:
B1(int i ) //基类构造函数
{
cout<<"B1构造函数被调用";
x=i;
cout<<"x="<<x<<endl;
}
~B1()
{
cout<<"B1析构函数被调用"<<endl;
}
protected:
int x;
};
class B2
{
public:
B2(int j) //基类构造函数
{
cout<<"B2构造函数被调用";
y=j;
cout<<"y="<<y<<endl;
}
~B2()
{
cout<<"B2析构函数被调用"<<endl;
}
protected:
int y;
};
class B3
{
public:
B3() //基类构造函数
{
cout<<"B3构造函数被调用"<<endl;
}
~B3()
{
cout<<"B3析构函数被调用"<<endl;
}
};
class C:public B1,public B2,public B3
{
public:
C(int a,int b,int c,int d):B1(a),ob1(b),ob2(c),B2(d) //B3中没有数据成员,可以不写
{}
private:
B1 ob1; //定义子对象
B2 ob2;
B3 ob3;
};
int main()
{
C obj(1,2,3,4);
return(0);
}
运行结果:
B1构造函数被调用x=1
B2构造函数被调用y=4
B3构造函数被调用
B1构造函数被调用x=2
B2构造函数被调用y=3
B3构造函数被调用
B3析构函数被调用
B2析构函数被调用
B1析构函数被调用
B3析构函数被调用
B2析构函数被调用
B1析构函数被调用
先调用基类B1,B2,B3的构造函数,再调用C的对象成员ob1,ob2,ob3的构造函数。析构函数执行次序与构造函数相反。
多继承和多层次继承的二义性
在多继承情况下,如果在派生类中使用的某个成员名在多个基类中出现,而在派生类中没有定义,这时使用该成员名就会出现二义性问题。采用虚基类可以解决此问题。虚基类的初始化不是在其直接派生类中进行,而是在其间接派生类中进行的。首先调用虚基类的构造函数,然后是非虚基类的构造函数,最后是派生类的构造函数。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(string nam,string s,int a)
{
name=nam;
sex=s;
age=a;
}
protected:
string name;
string sex;
int age;
};
class Teacher:virtual public Person
{
public:
Teacher(string nam,string s,int a,string t):Person(nam,s,a)
{
title=t;
}
protected:
string title;
};
class Student:virtual public Person
{
public:
Student(string nam,string s,int a,float sco):Person(nam,s,a)
{score=sco;}
protected:
float score;
};
class Graduate:public Teacher,public Student
{
public:
Graduate(string nam,string 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<<"sex:"<<sex<<endl;
cout<<"age:"<<age<<endl;
cout<<"score:"<<score<<endl;
cout<<"title:"<<title<<endl;
cout<<"wages:"<<wage<<endl;
}
private:
float wage;
};
int main()
{
Graduate grad1("张三","男",24,"助教",90,2250.5);
grad1.show();
return(0);
}
运行结果:
name:张三
sex:男
age:24
score:90
title:助教
wages:2250.5
类Person作为类Teacher和类Student的公共基,类Teacher和类Student共同派生类Graduate。
程序执行时,先对虚基类Person初始化,然后对类Teacher和类Student初始化虽然这两个类时虚基类Person的直接派生类,他们的参数表中都包含公共基类Person的参数项,但不会再对类Person进行初始化,这样就会避免虚基类的重复初始化。