继承
定义一个派生类的过程:
- 吸收基类的成员
- 添加新的成员(非必须)
- 隐藏基类的成员(非必须)
//基类
class point{
public:
point(int _x=0,int _y=0):x(_x),y(_y){
cout << "call point" << endl;
}
void print() const{
cout << x << "," << y << endl;
}
protected:
int x, y;
};
//继承
class point3D:public point{
public:
point3D(int _x=0,int _y=0,int _z=0):point(_x,_y),z(_z){
cout << "call point3D" << endl;
}
void disPlay() const{
print();
//派生类可以访问基类的protected成员
cout <<x<<","<<y<<","<< z << endl;
}
private:
int z;
};
int main(){
point3D pt(1, 4, 6);
pt.disPlay();
}
访问权限总结
B继承A,C继承B
class A{ public: A(int _x=0):x(_x){ } void showA() { cout << x << endl; } private: int x; }; //B继承A class B:public A{//公有继承 public: B(int _x=0,int _y=0):A(_x),y(_y){ } void showB(){ showA(); cout << y << endl; } private: int y; }; class C:public B{//公有继承 public: C(int _x=0,int _y=0,int _z=0):B(_x,_y),z(_z){} void showC(){ showB(); cout << z << endl; } private: int z; }; int main(){ C tmp(10, 99, 77); tmp.showC(); }
私有继承和保护继承都可以让派生类访问基类的非私有成员,但是私有继承会影响多次继承对基类的访问
- 不管什么继承方式,
派生类内部
都不能
访问基类的私有成员
; - 不管什么继承方式,
派生类内部
除了基类的私有成员
不可以访问,其他的都可以访问
; - 不管什么继承方式,派生类对象
在类外
除了公有继承基类中的公有成员
可以访问外,其他的都不能访问。
继承关系的局限性
- 创建和销毁类对象不能被继承-----构造函数和析构函数
- 复制操作不能被继承----拷贝构造和赋值运算(operator =)
- 空间分配不能被继承----operator new()和operator delete()
- 友元函数不能被继承
因为四大函数(构造+析构+拷贝构造+重载=)肯定可以访问所有的成员,所以不允许继承
拷贝构造就是用一个已有的给新的赋值,赋值操作就是用已有的给已有的赋值
友元函数破坏了封装性,为了降低影响,一定不能继承
问:创建派生类对象时,先调用基类构造函数,再调用派生类构造函数
错误:派生类会执行自己的构造函数,只不过会中途调用基类构造函数
derive():
base()
{}----只不过不是显式调用
含有其他类对象成员
显式调用基类构造函数,写的是基类类名;显式调用对象成员的构造函数,写的是对象成员的名字。
#include <bits/stdc++.h> using namespace std; //基类 class Base{ public: Base(int x):_base(x){} void display() const{ cout << _base << endl; } private: int _base; }; //其他test类 class Test{ public: Test(int x):_test(x){} void display() const{ cout << _test << endl; } private: int _test; }; //派生类 class Derived:public Base{ public: /*注意继承基类的构造函数用类名,普通类的构造函数用对象名*/ Derived(int x,int y,int z):Base(x),_Derived(y),obj(z){} void display() const{ Base::display(); obj.display(); cout << _Derived << endl; } private: int _Derived; //派生类有其他类对象 Test obj; }; int main(){ Derived d(10, 20, 30); d.display(); }
拷贝构造函数
#include <bits/stdc++.h> using namespace std; //基类 class Base{ public: Base(int x,int y):a(x),b(y){} Base(const Base &m):a(m.a),b(m.b){} void display() const{ cout << a << "," << b << endl; } private: int a,b; }; //其他test类 class Test{ public: Test(char m,char n):c(m),d(n){} Test(const Test& op):c(op.c),d(op.d){} void display() const{ cout << c << "," << d << endl; } private: char c,d; }; //派生类 class Derived:public Base{ public: Derived(Base ba,int z,Test op):Base(ba),val(z),obj(op){} void display() const{ Base::display(); obj.display(); cout << val << endl; } private: int val; //派生类有其他类对象 Test obj; }; int main(){ Test op('c', 'c'); Base ba(100, 200); Derived tmp(ba,3,op); tmp.display(); }
运行结果:
100,200
c,c
3
基类对象作为派生类的私有成员,也依然按照
显示调用对象成员
那样初始化//基类 class Base{ public: Base(int x,int y):a(x),b(y){} void display() const{ cout << a << "," << b << endl; } private: int a,b; }; //派生类 class Derived:public Base{ public: //注意:哪怕是基类作为对象成员,也要用对象名 Derived(int a,int b,int c,int d,int e):Base(a,b),obj(c,d),val(e){} void display() const{ Base::display(); obj.display(); cout << val << endl; } private: int val; Base obj;//基类对象 };
一个派生类对象销毁时,调用自己的析构函数,析构函数执行完后,按照对象成员的声明顺序的逆序去调用对象成员的析构函数,最后调用基类的析构函数
对于基类成员的隐藏
从内存角度,派生类会继承基类所有的成员(包括私有成员,因为字节数是包含的),只不过会按照继承属性进行访问
不管是数据成员还是成员函数,只要派生类和基类发生重名,就会对基类同名成员进行隐藏
//基类 class Base{ public: Base(int x,int y):a(x),b(y){} long arg = 1000; void print() const{ cout << a << "," << b << "," << arg << endl; } private: int a,b; }; //派生类 class Derived:public Base{ public: Derived(int a,int b,int e):Base(a,b),c(e){} long arg = 200;//与基类arg名称相同,基类该成员被隐藏 void print() const{//与基类print()名称相同,基类该函数被隐藏 cout <<c<< "," << arg << endl; } private: int c; }; int main(){ Derived m(9, 8, 7); //输出的是派生类的结果:200 cout << m.arg << endl; //加上作用域输出的是基类结果:1000 //不建议使用,因为破坏了封装性 cout << m.Base::arg << endl; m.print();//输出派生类的结果 }
注意:如果派生类没有print,调用print是利用基类去调用,此时输出的同名arg结果还是基类的arg
//基类 class Base{ public: Base(int x,int y):a(x),b(y){} long arg = 1000; void print() const{ cout << a << "," << b << "," << arg << endl; } private: int a,b; }; //派生类 class Derived:public Base{ public: Derived(int a,int b,int e):Base(a,b),c(e){} long arg = 200; private: int c; }; int main(){ Derived m(9, 8, 7); m.print();//9,8,1000 }
只要是成员函数名相同,不管参数和返回值是否一样,基类相同函数名照样被隐藏
//基类
class Base{
public:
Base(int x,int y):a(x),b(y){}
void print() const{
cout << a << "," << b << endl;
}
private:
int a,b;
};
//派生类
class Derived:public Base{
public:
Derived(int a,int b,int e):Base(a,b),c(e){}
int print() const{
cout << c << endl;
return 98;
}
private:
int c;
};
int main(){
Derived m(9, 8, 7);
m.print();//一定会调用派生类,基类函数被隐藏
}
基类和派生类的转换
一般情况下,派生类的内存空间比基类大。
如果都是空类,内存空间有可能相同,涉及平台占位机制的实现
//基类 class Base{ public: Base(int x,int y):a(x),b(y){} void print() const{ cout << a << "," << b << endl; } private: int a,b; }; //派生类 class Derived:public Base{ public: Derived(int a,int b,int e):Base(a,b),c(e){} private: int c; };
主函数
int main(){ Base _base(11,22); Derived _derived(9, 8, 7); /*把一个基类对象赋值给一个派生类对象❓*/ //_derived = _base;❌ /*把一个派生类对象赋值给一个基类对象❓*/ _base = _derived; /*可否将一个基类指针指向一个派生类对象❓*/ Base *pbase = &_derived; /*将一个派生类指针指向一个基类对象❓*/ //Derived *pderived = &_base;❌ /*将一个基类引用绑定一个派生类对象❓*/ Base& tmp = _derived; /*将一个派生类引用绑定一个基类对象❓*/ //Derived &der = _base;❌ }
从内存角度,基类指针指向派生类对象,由于基类指针访问内存大小小于派生类内存,所以会存在访问不到的问题,但是程序可以通过;相反利用派生类指针访问基类对象,如果访问派生类多出基类的内存访问,相当于非法访问,这时候是不被允许的。
Derived类的指针指向Base对象,除了操纵Base对象的空间,还需要操纵一片空间,只能是非法空间,所以会报错。
向上转型是可行的,也就是基类的指针引用是左值
往基类方向的转型就叫向上转型
当base指针或引用指向派生类时,向下转型是可行的
dynamic_cast
<指针类型>****(指针名称)
//基类
class Base{
public:
Base(int x,int y):a(x),b(y){}
virtual void display(){
cout << a << "," << b << endl;
}
private:
int a,b;
};
//派生类
class Derived:public Base{
public:
Derived(int a,int b,int e):Base(a,b),c(e){}
private:
int c;
};
int main(){
Base _base(11,22);
Derived _derived(9, 8, 7);
Base *pb = &_derived;//向上转型,基类指针指向派生类
//利用dynamic_cast可以把基类指针转换成派生类
//因为pb本来就是指向派生类对象,所以可以把Base*变成Derived*
//但是前提是:Base类中存在虚函数virtual
Derived *pd = dynamic_cast<Derived *>(pb);
pd->display();
}
失败的转换
Base _base(11,22); Derived _derived(9, 8, 7); Base *pb = &_base; //转换失败,因为pb本身就是一个指向base对象的,转换成派生类一定存在非法访问 Derived *pd = dynamic_cast<Derived *>(pb); if(pd){ cout << "success" << endl; }else{ cout << "fails" << endl; }
复制控制函数
复制控制函数就是 拷贝构造函数、赋值运算符函数
给派生类手动定义复制控制函数,注意在其中显式调用相应的基类的复制控制函数
总结:
-
如果类的数据成员申请了
堆空间
,那么必须手动写出该类的复制控制函数 -
如果父类的数据成员申请了堆空间,那么父类的复制控制函数必须显式定义,派生类类自身的数据成员如果没有申请堆空间,不用显式定义复制控制函数
#include <bits/stdc++.h>
using namespace std;
//基类
class Base{
public:
Base(int x,int y):a(x),b(y){}
Base(const Base& op):a(op.a),b(op.b){}
Base& operator=(const Base& op){
a = op.a;
b = op.b;
return *this;
}
virtual void display() const { cout << a << "," << b << ","; }
private:
int a,b;
};
//派生类
class Derived:public Base{
public:
Derived(int a,int b,int e):Base(a,b),c(e){}
Derived(const Base& op,int e):Base(op),c(e){}
Derived& operator=(const Derived& op){
Base::operator=(op);
c = op.c;
return *this;
}
void display() const{
Base::display();
cout << c << endl;
}
private:
int c;
};
int main(){
Base ba(100, 200);
Base bb = ba;
Base bc(ba);
Derived da(11, 22, 33);
Derived db(ba, 44);
Derived dc = da;
//向上转型
Base &call = da;
call.display();
Base *p = &db;
p->display();
//向下转型
Derived *point = dynamic_cast<Derived *>(p);
point->display();
Derived& m = dynamic_cast<Derived&>(call);
m.display();
}
不管是基类的运算符重载还是派生类的运算符重载:
- 函数返回类型必须是:本类名的引用
- 函数的参数类型必须是:
const
的本类名的引用
拷贝构造函数
class student{ public: student(int a,int b):aa(a),bb(b){} student(const student& stu){ this->aa = stu.aa; this->bb = stu.bb; } private: int aa, bb; }; class graduate:public student{ public: graduate(int a,int b,string c):student(a,b),str(c){} //拷贝构造函数 //赋值兼容:子类对象可以给父类对象赋值 //因为拷贝构造器不可以继承,只能放在拷贝构造列表里 graduate(const graduate& gra):student(gra),str(gra.str){ } private: string str; };
运算符重载
class student{ public: student(int a,int b):aa(a),bb(b){} student& operator=(const student& stu){ if(this == &stu)//是否自我赋值 return *this; this->aa = stu.aa; this->bb = stu.bb; return *this; } private: int aa, bb; };
class graduate:public student{ public: graduate(int a,int b,string c):student(a,b),str(c){} graduate& operator=(const graduate& gra){ if(this==&gra) return *this; //用子类的赋值重载,父子类同名函数会发生shadow(只看函数名) student::operator=(gra); this->str = gra.str; return *this; } private: string str; };
注意:派生类调用基类的运算符重载函数需要加上作用
友元函数
友元函数不是类成员,因引不能被继承
派生类的友元函数
能使用基类中的友元函数
的方法:
通过强制类型转换
,将派生类的指针或引用强转为其类的引用或指针,然后使用转换后的引用或是指针来调用基类中的友元函数。
- 一个友元作为输出函数的例子
//基类
class Base{
public:
Base(int x,int y):a(x),b(y){}
//友元输出函数
friend ostream &operator<<(ostream &, Base &);
private:
int a,b;
};
//友元输出函数定义
ostream &operator<<(ostream &os, Base &op){
os << op.a << "," << op.b;
return os;
}
//派生类
class Derived:public Base{
public:
Derived(int a,int b,int e):Base(a,b),c(e){}
private:
int c;
};
int main(){
Base a(11, 22);
Derived b(33, 44, 55);
cout << a << endl;
cout << b << endl;
}
运行结果:
派生类可以调用基类的友元函数
因为发生了
赋值兼容
,也就派生类成员b向上转型
了基类对象a.
- 派生类强制类型转换可以调用基类同名函数
#include <bits/stdc++.h>
using namespace std;
//基类
class Base{
public:
Base(int x,int y):a(x),b(y){}
//友元输出函数
friend ostream &operator<<(ostream &, Base &);
private:
int a,b;
};
//友元输出函数定义
ostream &operator<<(ostream &os, Base &op){
os << op.a << "," << op.b;
return os;
}
//派生类
class Derived:public Base{
public:
Derived(int a,int b,int e):Base(a,b),c(e){}
//友元输出函数
friend ostream &operator<<(ostream &, Derived &);
private:
int c;
};
//派生类的友元输出函数
ostream &operator<<(ostream &os, Derived &m){
//调用基类的友元函数
os << static_cast<Base &>(m)<<",";
os << m.c;
return os;
}
int main(){
Base a(11, 22);
Derived b(33, 44, 55);
cout << a << endl;
cout << b << endl;
}
输出结果:
或者:
os << dynamic_cast<Base &>(m)<<",";