C++--被遗弃的多重继承、经典问题

一.被遗弃的多重继承

Q:C++中是否允许一个类继承自多个父类?
在实际的C++编译环境中,C++是支持编写多重继承的代码
1.一个子类可以拥有多个父类
2.子类拥有所有父类的成员变量
3.子类继承所有父类的成员函数
4.子类对象可以当作任意父类对象使用
多重继承的语法规则

但是在多重继承中会存在许多问题
Q:多重继承得到的对象可能拥有不同的地址
代码示例

#include <iostream>
#include <string>

using namespace std;

class BaseA
{
    int ma;
public:
    BaseA(int a)
    {
        ma = a;
    }
    int getA()
    {
        return ma;
    }
};

class BaseB
{
    int mb;
public:
    BaseB(int b)
    {
        mb = b;
    }
    int getB()
    {
        return mb;
    }
};

class Derived : public BaseA, public BaseB//拥有两个父类
{
    int mc;
public:
    Derived(int a, int b, int c) : BaseA(a), BaseB(b)
    {
        mc = c;
    }
    int getC()
    {
        return mc;
    }
    void print()
    {
        cout << "ma = " << getA() << ", "
             << "mb = " << getB() << ", "
             << "mc = " << mc << endl;
    }
};

int main()
{
    cout << "sizeof(Derived) = " << sizeof(Derived) << endl;   

    Derived d(1, 2, 3);

    d.print();

    cout << "d.getA() = " << d.getA() << endl;
    cout << "d.getB() = " << d.getB() << endl;
    cout << "d.getC() = " << d.getC() << endl;

    cout << endl;
    //两个父类指针指向同一个对象
    BaseA* pa = &d;
    BaseB* pb = &d;

    cout << "pa->getA() = " << pa->getA() << endl;
    cout << "pb->getB() = " << pb->getB() << endl;

    cout << endl;

    void* paa = pa;
    void* pbb = pb;

    if( paa == pbb )
    {
        cout << "Pointer to the same object!" << endl;
    }
    else
    {
        cout << "Error" << endl;
    }

    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "paa = " << paa << endl;
    cout << "pbb = " << pbb << endl; 

    return 0;
}

运行结果

我们可以看到在条件判断语句中,paa与pbb指向的是同一个对象,按理说应该打印相同时的语句,可是却打印错误,从接下来的地址打印结果是不一样的,这就造成了多重继承的问题--同一个对象取地址之后进行初始化,但是pa与pb打印的结果却不同

Q:多重继承可能产生冗余的成员

代码实现

#include <iostream>
#include <string>

using namespace std;

class People
{
    string m_name;
    int m_age;
public:
    People(string name, int age)
    {
        m_name = name;
        m_age = age;
    }
    void print()
    {
        cout << "Name = " << m_name << ", "
             << "Age = " << m_age << endl;
    }
};

class Teacher : virtual public People
{
public:
    Teacher(string name, int age) : People(name, age)
    {
    }
};

class Student : virtual public People
{
public:
    Student(string name, int age) : People(name, age)
    {
    }
};

class Doctor : public Teacher, public Student
{
public:
    Doctor(string name, int age) : Teacher(name, age), Student(name, age), People(name, age)
    {
    }
};

int main()
{
    Doctor d("Delphi", 33);

    d.print();

    return 0;
}


由运行结果可以知道,在进行print时出错,这是因为Doctor由于继承,会有两个打印的函数,所以通过作用域分辨符进行改正
由于冗余,打印的结果可能不一样

当多重继承关系出现闭合时将产生暑假冗余的问题
解决方案:虚继承

二.经典问题分析

一.关于动态内存分配
Q:new和malloc的区别?delete和free的区别?
A.new关键字与malloc函数的区别
1.new关键字是C++的一部分,malloc是由C库提供的函数
2.new以具体类型为单位进行内存分配,malloc以字节为单位进行内存分配
3.new在申请内存空间时可进行初始化,malloc仅根据需要申请定量的内存空间
代码示例

#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

class Test
{
    int* mp;
public:
    Test()
    {
        cout << "Test::Test()" << endl;

        mp = new int(100);

        cout << *mp << endl;
    }
    ~Test()
    {
        delete mp;

        cout << "~Test::Test()" << endl;
    }
};

int main()
{
    Test* pn = new Test;
    Test* pm = (Test*)malloc(sizeof(Test));

    delete pn;
    free(pm);

    return 0;
}

运行结果

关于动态内存分配
new和malloc的区别
1.new在所有C++编译器中都能被支持,malloc在某些系统开发中是不能调用的
2.new能够触发析构函数的调用,malloc仅分配需要的内存空间
3.对象的创建只能使用new,malloc不适合面向对象开发
delete和free的区别
1.delete在所有C++编译器中都被支持,free在某些系统开发中是不能调用的
2.delete能够触发析构函数的调用,free仅归还之前的分配空间
3.对象的销毁只能使用delete,free不适合面向对象的开发

B.关于虚函数
Q:构造函数是否可以成为虚函数?析构函数是否可以成为虚函数?
1.构造函数不可能成为虚函数--在构造函数执行结束后,虚函数表指针才会被正确的初始化
2.析构函数可以成为虚函数--建议在设计类时将析构函数声明为虚函数

Q:构造函数中是否可以发生多态?析构函数是否可以发生多态?
1.构造函数中不可能发生多态行为--在构造函数执行时,虚函数表指针未被正确初始化
2.析构函数在不可能发生多态行为--在析构函数执行时,虚函数表指针已经被销毁
C.关于继承中的强制类型转换
1.dynamic_cast是与继承相关的类型转换关键字,并且要求相关的类中必须有虚函数
2.用于直接或者间接继承关系的指针之间

编译器会检查dynamic_cast的使用是否正确,类型转换的结果只可能在运行的阶段才能得到
代码示例

#include <iostream>
#include <string>

using namespace std;

class Base
{
public:
    Base()
    {
        cout << "Base::Base()" << endl;
    }

    virtual ~Base()//析构虚函数
    {
        cout << "Base::~Base()" << endl;
    }
};

class Derived : public Base
{

};

int main()
{
    Base* p = new Derived;//指向的是子类对象

    Derived* pd = dynamic_cast<Derived*>(p);//强制转换

    cout<<"pd="<<pd<<endl;

    if( pd != NULL )
    {
        cout << "pd = " << pd << endl;
    }
    else
    {
        cout << "Cast error!" << endl;
    }

    delete p;

    return 0;
}

将其修改之后的打印结果比较


我们可以知道不能将子类指针对象指向父类,产生的对象时无效的

原文地址:https://blog.51cto.com/13475106/2420013

时间: 2024-10-07 06:32:45

C++--被遗弃的多重继承、经典问题的相关文章

第53课 被遗弃的多重继承(上)

1. 单一继承 (1)实验代码 #include <iostream> #include <string> using namespace std; void visitVtbl(int **vtbl) { cout << vtbl << endl; cout << "\t[-1]: " << (long)vtbl[-1] << endl; typedef void (*FuncPtr)(); int

第54课 被遗弃的多重继承(下)

1. C++中的多重继承 (1)一个子类可以拥有多个父类 (2)子类拥有所有父类的成员变量 (3)子类继承所有父类的成员函数 (4)子类对象可以当作任意父类对象使用 (5)多重继承的语法规则 class Derived: public BaseA, public BaseB, public BaseC{…}; 2. 多重继承问题一 (1)通过多重继承得到的对象可以拥有“不同的地址”!!! (2)解释方案:无 (3)原因分析 [编程实验]多重继承问题一 #include <iostream> u

第五十三课、被遗弃的多重继承(上)

一.c++的多重继承 1.c++支持编写多重继承的代码 (1).一个子类可以拥有多个父类 (2).子类拥有所有父类的成员变量 (3).子类继承所有父类的成员函数 (4).子类对象可以当做任意父类对象使用 2.多重继承产生的问题一:通过多重继承得到的对象拥有不同的地址 #include<iostream> using namespace std; class BaseA { protected: int ma; public: BaseA(int a) { ma = a; } int GetA(

第53课 被遗弃的多重继承

问题:C++中是否允许一个类继承自多个父类呢?答案是肯定的,这种现象就是多重继承多重继承是C++中一个特有的特性,因为在其他的程序设计语言里面,如C#.java等语言只支持单重继承 C++支持编写多重继承的代码-一个子类可以拥有多个父类-子类拥有所有父类的成员变量-子类继承所有父类的成员函数-子类对象可以当作任意父类对象使用 多重继承的语法规则 class Derived: public BaseA, public BaseB, public BaseC { // .... }; 多重继承的本质

第53课 被遗弃的多重继承 (中)

多重继承的问题三:多重继承可能产生多个虚函数表 #include <iostream> using namespace std; class BaseA { public: virtual void funcA() { cout << "BaseA::funcA()" << endl; } }; class BaseB { public: virtual void funcB() { cout << "BaseB::funcB(

第53课 被遗弃的多重继承 (下)——正确的使用多重继承

工程开发中的多重继承方式:(这是面向对象理论中所推荐的方式)单继承某个类 + 实现(多个)接口 #include <iostream> #include <string> using namespace std; class Base { protected: int mi; public: Base(int i) { mi = i; } int getI() { return mi; } bool equal(Base* obj) { return (this == obj);

秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题

版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 秒杀多线程第十五篇 关键段,事件,互斥量,信号量的“遗弃”问题 在<秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量>中对经典多线程同步互斥问题进行了回顾和总结,这篇文章对Windows系统下常用的线程同步互斥机制——关键段.事件.互斥量.信号量进行了总结.有网友问到互斥量能处理“遗弃”问题,事件和信号量是否也能处理“遗弃”问题.因此本文将对事件和信号量作个试验,看看事件和信号量能否处理“遗弃”问题. 一.

面向对象【day07】:类的属性-继承-经典类

本节内容 类的公有属性 析构函数 类的继承 新式类和经典类 一.类的公有属性 一.概述 前面我们讲了类的私有属性,现在我们来说说类的公有属性,这边很容易被人弄混淆,有人觉的,在__init__()构造方法中,除了私有属性,其他的都是公有属性了,其实这是一个错误的结论,并不是定义在__init__()初始化方法中的属性是公有属性(除私有属性),那什么是公有属性呢?揭起了大家的好奇心. 定义:指的是所属这个类的所有对象,都可以访问的属性,叫做公有属性. 二.公有属性 2.1 定义 说明:在类中直接定

部分经典IT书籍

[系统,网路管理]1) Learning the Unix Operating System 1565923901 O'reilly/1997-4ed ***强力推荐给想入门unix的网友们!! 就只有薄薄的不到100页,在书局偶然发现....深觉其内容适合初学者 相信只要读它,一个下午就可以了解基本UNIX的操作!! 2) Unix : Visual Quickstart Guide 0201353954 A-W/1998-1ed ***强力推荐给想入门unix的网友们!! 比上面那本大了点,