一.原则:
父类/子类
基类/派生类
二.语法:
class 派生类 : [访问限定符] 基类 {
成员
}
例:(成员函数)
#include<iostream>
using namespace std;
class Father{
public:
Father(){
cout << __func__ << endl;
}
~Father(){
cout << __func__ << endl;
}
void FatherTest(){
cout << __func__ << endl;
}
};
class Son:public Father{
public:
Son(){
cout << __func__ << endl;
}
~Son(){
cout << __func__ << endl;
}
};
int main(){
Son s;
s.FatherTest();
}
输出:
Father
Son
FatherTest
~Son
~Father
先调用Father的构造函数,先调用son的析构函数
注:
当父类中只有带参构造函数和成员变量(公有),子类只有默认构造函数,则不可编译。
1.但可以使子类的构造函数中调用父类的带参构造函数
Son():Father(10){ }
2.也可以子类构造函数含参数传到父类中,让父类去构造
Son(int n):Father(n)
构造子类对象时:Son s(100);
3.可以继承的基础上再继承 ,例:三角形
#include<iostream>
#include<cmath>
using namespace std;
class Triangle{
int a,b,c;
public:
Triangle(int a,int b,int c):a(a),b(b),c(c){}//构造
int GetLength()const{
return a+b+c;
}
float GetArea()const{
float h = GetLength()/2.0;
return sqrt(h*(h-a)*(h-b)*(h-c));
}
};
class IsoscelesTriangle:public Triangle{//等腰三角形 (继承三角形)
public:
IsoscelesTriangle(int bottom,int side):Triangle(bottom,side,side){}
};
class EquilteralTriangle:public IsoscelesTriangle{//等边三角形(继承等腰三角形)
public:
EquilteralTriangle(int side):IsoscelesTriangle(side,side){}
};
int main(){
Triangle t(3,4,5);
cout << t.GetLength() << endl;
cout << t.GetArea() << endl;
EquilteralTriangle et(3);
cout << et.GetLength() << endl;
cout << et.GetArea() << endl;
IsoscelesTriangle it(4,7);
cout << it.GetLength() << endl;
cout << it.GetArea() << endl;
}
三.protected
作用:让派生类能够访问公众不能使用的内部函数
子类内部访问父类成员,只能访问public
和protected
成员。
也继承了private成员,但是不能访问。
继承方式:
public
无论类内部还是类对象都可以访问。protected
类对象不可访问,类内部与继承类的内部可以访问。private
只有类内部可以访问。- 简看:
四.同名隐藏规则(平时最好不要重名)
成员变量同名,会隐藏基类。
#include<iostream>
using namespace std;
class Base{
public:
int n;
void PrintBase(){
cout << &n << ":" << n << endl;
}
};
class Derive:public Base{
public:
int n;
void PrintDerive(){
cout << &n << ":" << n << endl;
}
};
int main(){
Derive d;
d.n = 100;
d.PrintBase() ;
d.PrintDerive() ;
}
输出为:0x6ffe10:0
0x6ffe14:100
要想避免这种情况,可以专门给基类赋值
d.Base::n = 10;
成员函数同名 ,大体与成员变量情况相同。
注:假如父类中有同名重载函数(函数名相同,参数不相同),子类中没有。当直接调用该函数时会出错,因为一旦在子类中找到重名函数,就不会再去父类中找重载函数,只有当子类中没有重名函数,才会再去父类中找函数。解决方法:
①在子类中写:using Base::Test;
②在main中写:d.Base::Test(20);
五.赋值兼容原则
概念:在任何需要基类(父类)对象的地方都可以使用公有的派生类(子类)对象来代替。反之,不可。
1.子类的对象可以直接赋值给父类的对象。
Base base;
Derive derive;
base = derive;
2.子类的对象可以初始化父类的引用。
Derive derive;
Base& base = derive;
3.子类对象的地址可以赋给指向父类的指针,指向父类对象的指针变量也可以指向子类对象。
Derive derive;
Base* base = &derive;
注:还可以用此来解决同名隐藏
Base& f = d;//d是子类对象
f.Test(); //此时调用的是父类的
//or:
Base* p = d;
p->Test();
六.补充:
当子类和父类有拷贝构造函数和赋值运算符重载函数时,在子类中需要负责父类的拷贝和赋值
(最好使用默认的)
Derice(const Derice& d):Base(d){
n = d.n;
}
Derice& operator=(const Derice&){
n = d.n;
Base& b = *this;
b = d; //赋值兼容
return* this;
}
七.多重继承(java不允许)
class 类名 : public 基类1,public 基类2{
};
#include<iostream>
using namespace std;
class Base1{
int n;
public:
Base1(int n):n(n){}
void PrintN(){
cout <<__func__<< "(" << n << ")" << endl;
}
};
class Base2{
int m;
public:
Base2(int m):m(m){}
void PrintM(){
cout <<__func__<< "(" << m << ")" << endl;
}
};
class Derive:public Base1,public Base2{
public:
Derive(int n,int m):Base1(n),Base2(m){
}
};
int main(){
Derive d(1,2);
d.PrintN() ;
d.PrintM() ;
}
输出:PrintN(1)
PrintM(2)
1.两个父类中不允许出现重名函数
2.钻石继承/菱形继承
例:B,C继承了A,D继承了B,C。下列代码会出现什么情况?
#include <iostream>
using std::cout;
using std::endl;
class A{
public:
void test();
private:
int id;
};
void A::test(){
cout << __func__ << endl;
}
class B : public A {};
class C : public A {};
class D : public B,public C{};
int main(){
cout << "A size:" << sizeof(A) << endl;
cout << "B size:" << sizeof(B) << endl;
cout << "C size:" << sizeof(C) << endl;
cout << "D size:" << sizeof(D) << endl;
D d;
d.test();
}
会报错,不确定test()调用的是B的还是C的,应在前加上访问限定符,d.B::test()
- 虚继承
为了节省空间,可以将B、C对A的继承定义为虚拟继承,而A就成了虚拟基类
class A;
class B:vitual public A;
class C:vitual public A;
class D:public B,public C;
子类构造时初始化虚基类(假如有成员时)
Derice(int n,int m):n(n),Base1(m),Base2(m),Base(m){}
此时就可以调用重名函数,不用加访问限定符