一. 被遗弃的多重继承
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; }
将其修改之后的打印结果比较
我们可以知道不能将子类指针对象指向父类, 产生的对象时无效的
来源: http://www.bubuko.com/infodetail-3123095.html