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);
 //输出的是派生类的结果: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)<<",";

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值