【C++】5.继承与派生(下)

本文详细介绍了C++中的派生类构成,包括构造和析构顺序,以及如何处理多重继承中的菱形问题,重点讲解了虚继承的概念及其优势。内容涵盖了基础的面向对象特性在实际编程中的应用。
摘要由CSDN通过智能技术生成


5.3父子关系

派生类的构成

  1. 派生类的构成:派生类就是子类 ,他会继承除了父类的析构和构造函数之外所有的成员函数和数据成员。

    也就是说构造析构不继承(因为有低保吃,不要继承),其他的继承。

  2. 在子类里面是可以添加自己新的成员的,这些就需要通过子类的对象来调用了。父类是不能调用你这些新增的成员的。

  3. 如果你的子类中添加的成员和父类中的成员名字相同的话,那么子类就会把父类的成员隐藏起来。但是你要用的时候还是可以用的,用的方式是通过父类的类名加上作用域符,来访问这个父类和子类同名的成员。如果继承的是多个父类,多个父类中也有名字相同的成员,那么也可以通过这个名字加作用域符的形式来访问。

单个父类:

class Father {
public:
    Father();
    ~Father();
    int val;
    int num;           //这里有个num
};
class Son :public Father{
public:
    Son();
    ~Son();
    int num;           //这里也有个num
};

int main() {
    Son obj_son;
    obj_son.num;//这个访问的num是哪个呢?
    obj_son.val;

    return 0;
}

Father::Father() {}
Father::~Father() {}
Son::Son() {}
Son::~Son() {}
class Father {
public:
    Father();
    ~Father();
    int val;
    int num;         //这个num=29
};
class Son :public Father {
public:
    Son();
    ~Son();
    int num;         //这个num=99
};

int main() {
    Son obj_son;
    cout << "obj_son.num = " << obj_son.num << endl;//打印99,这个num是子类的num
    cout << "obj_son.Father::num = " << obj_son.Father::num << endl;//这个num是父类的num

    return 0;
}

Father::Father() {
    val = 10;
    num = 20;
}
Father::~Father() {}
Son::Son() {
    num = 99;
}
Son::~Son() {}

多个父类:

class Father {
public:
    Father();
    ~Father();
    int val;
    int a;
};
class Father_1 {
public:
    Father_1();
    ~Father_1();
    int val;
    int b;
};
class Son :public Father, public Father_1 {   //继承两个父类
public:
    Son();
    ~Son();
    int num;
    int val;
};

int main() {
    Son obj_son;
    cout << "obj_son.val = " << obj_son.val << endl;
    cout << "obj_son.Father::val = " << obj_son.Father::val << endl;
    cout << "obj_son.Father_1::val = " << obj_son.Father_1::val << endl;

    return 0;
}

Father::Father() {
    val = 10;
    a = 1;
}
Father::~Father() {}

Father_1::Father_1() {
    val = 20;
    b = 2;
}
Father_1::~Father_1() {}

Son::Son() {
    num = 99;
    val = 88;
}
Son::~Son() {}

派生类和基类的关系

子类是父类的对象,但是父类对象不是子类的对象。

也就是子类的对象可以当作父类的对象来使用。因为子类是继承了父类的所以成员的,也就是说父类有的,子类也有。用父类的时候,既然所有数据子类都有,那么直接用这个子类也可以。

也可以说父类对象是子类对象,但是子类对象不是父类对象。

class Father {
public:
    Father();
    ~Father();
};

class Son :public Father {
public:
    Son();
    ~Son();  
};

int main() {
    Father obj_fat;//一个父类的对象
    Son obj_son;//一个子类的对象 

    obj_fat = obj_son;//父类对象能用子类对象赋值。
    //obj_son = obj_fat;这个不行//子类对象不能用父类对象赋值

    Father* p_fat;//一个父类的指针 
    Son* p_son;//一个子类的指针

    return 0;
}

Father::Father() {}
Father::~Father() {}

Son::Son() {}
Son::~Son() {}

父类对象不能用子类对象赋值。子类对象不能用父类对象赋值。

为什么呢?

在这里插入图片描述

因为父类填不满子类 。
在这里插入图片描述

子类能填满父类。


int main() {
    Father obj_fat;//一个父类的对象
    Son obj_son;//一个子类的对象 

    obj_fat = obj_son;//父类对象能用子类对象赋值。
    //obj_son = obj_fat;这个不行//子类对象不能用父类对象赋值

    Father* p_fat;//一个父类的指针
    p_fat = &obj_fat;//父类对象接收父类指针类型匹配,没问题
    p_fat = &obj_son;//父类对象接收子类指针,可以

    Son* p_son;//一个子类的指针
    p_son = &obj_son;//子类对象接收子类指针类型匹配,没问题
    //p_son = &obj_fat;//子类对象不能接收父类指针,不可以 

    return 0;
}

父类对象接收子类指针,可以。子类对象接收父类指针,不可以。

为什么呢?

在这里插入图片描述


派生类的构造析构顺序

class Father {
public:
    Father();
    ~Father();
    int num;
};

class Son :public Father {
public:
    Son();
    ~Son();
    int val;
};

int main() {
    Son obj_son;//一个子类的对象 
    cout << "obj_son.num = " << obj_son.num
        << "obj_son.val = " << obj_son.val
        << endl;

    return 0;
}

Father::Father() {
    num = 0;
    cout << "父类构造" << endl;
}
Father::~Father() {
    cout << "父类析构" << endl;
}

Son::Son() {
    val = 10;
    cout << "子类构造" << endl;
}
Son::~Son() {
    cout << "子类析构" << endl;
}

在这里插入图片描述

先构造的后析构。要创建子类对象,就要先调用父类的构造。


父类带参构造:

class Father {
public:
    Father();
    Father(int n);
    ~Father();
    int num;
};

class Son :public Father {
public:
    Son();
    Son(int n,int v);
    ~Son();
    int val;
};

int main() {
    Son obj_son(3,4);//一个子类的对象 
    cout << "obj_son.num = " << obj_son.num
        << "obj_son.val = " << obj_son.val
        << endl;

    return 0;
}

Father::Father() {
    num = 0;
    cout << "父类构造" << endl;
}
Father::Father(int n) {
    num = n;
    cout << "父类带参构造" << endl;
}
Father::~Father() {
    cout << "父类析构" << endl;
}

Son::Son() {
    val = 10;
    cout << "子类构造" << endl;
}
Son::Son(int n, int v) {
    num = n;
    val = v;    
    cout << "子类带参构造" << endl;
}
Son::~Son() {
    cout << "子类析构" << endl;
}

在这里插入图片描述


如果希望调用父类的构造函数

class Father {
public:
    Father();
    Father(int n);
    ~Father();
    const int num;
};

class Son :public Father {
public:
    Son();
    Son(int n, int v);
    ~Son();
    int val;
};

int main() {
    Son obj_son(3, 4);//一个子类的对象 
    cout << "obj_son.num = " << obj_son.num
        << "obj_son.val = " << obj_son.val
        << endl;

    return 0;
}
/* 父类 */
Father::Father() :num(0){         //num不能在构造函数中赋值,用成员初始化列表的方式给数据成员赋值
    cout << "父类构造" << endl;
}
Father::Father(int n) :num(n) {   //Father(int n)里面的n给了后面的num(n),用来更改num的值为n
    cout << "父类带参构造" << endl;
}
Father::~Father() {
    cout << "父类析构" << endl;
}
/* 子类 */
Son::Son() {
    val = 10;
    cout << "子类构造" << endl;
}
Son::Son(int n, int v) :Father(n) {
    val = v;
    cout << "子类带参构造" << endl;
}
/*
也可以写成这个
Son::Son(int n, int v) :Father(n),val(v) {
    cout << "子类带参构造" << endl;
}
*/
Son::~Son() {
    cout << "子类析构" << endl;
}

5.4菱形继承

在这里插入图片描述

A派生出A1和A2,然后A1和A2派生出AA。

A里有个n成员,派生的时候A1和A2里面分别有了一个n,然后之后AA里面就有了两个n,一个是A1里派生出来的,一个是A2里派生出来的。

这就显得臃肿了,很多用不到的其实是没用的。

class A {
public:
    int n;
};
class A_1 :public A {
public:
    int a1;
};
class A_2 :public A {
public:
    int a2;
};

class AA :public A_1,public A_2 {
public:
    int aa;
};

int main() {
    AA obj_aa;
    obj_aa.aa;
    obj_aa.a1;
    obj_aa.a2;
    //obj_aa.n;//这个不能直接访问了,因为不明确
    obj_aa.A_1::n;
    obj_aa.A_2::n;
    //obj_aa.A::n;//这个也不行,因为不是直接父类

    return 0;
}

虚继承

class A {
public:
    int n;
};
class A_1 :virtual public A {   //多了个virtual:虚指针
public:
    int a1;
};
class A_2 :virtual public A {   //多了个virtual:虚指针
public:
    int a2;
};

这里的virtual对于A_1和A_2没有任何影响,唯一的影响就是多了个虚指针。

而这个所谓的虚指针最大的作用就是标记这个A_1和A_2从A继承到了什么东西。

然后A_1和A_2派生出的AA时,就只给AA一份了。(给的这个是直接从A来的)

int main() {
    AA obj_aa;
    obj_aa.aa;
    obj_aa.a1;
    obj_aa.a2;
    obj_aa.n;//这个能直接访问了,这个n是A直接继承下来给AA的n
    obj_aa.A_1::n;
    obj_aa.A_2::n;
    obj_aa.A::n;//这个也能了,这个n是A的n

    return 0;
}

用虚指针A_1和A_2就会分别多出来4个字节的内存,用来存虚指针。用这4个字节来换取不知道多少个字节是不是很划算。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值