您的位置:首页 > 娱乐 > 八卦 > c++ 继承

c++ 继承

2024/12/23 5:09:00 来源:https://blog.csdn.net/weixin_47173597/article/details/141302291  浏览:    关键词:c++ 继承

继承


定义一个派生类的过程:

  1. 吸收基类的成员
  2. 添加新的成员(非必须)
  3. 隐藏基类的成员(非必须)
//基类
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);//输出的是派生类的结果:200cout << 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类中存在虚函数virtualDerived *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)<<",";

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com