C++中多重继承与虚继承资料的整理
最近在看C++的东西,看了好多多重继承的问题,有些说的很简单,有的说的很繁琐,把资料整理一下。在多重继承中,基类的构造函数的调用次序既不受派生类构造函数初始化列表中出现的基类构造函数的影响,也不受基类在构造函数初始化列表中的出现次序的影响,它按照基类在类派生列表中的出现次序依次调用相应的基类构造函数。析构顺序与构造顺序逆序进行。
多重继承中,派生类的指针或引用可以转换为其任意基类的指针或引用。因此,这种转换更可能遇到二义性问题。
在多重继承中,成员函数中使用的名字的查找首先在函数本身进行,如果不能在本地找到名字,就继续在成员的类中查找,然后同时(并行)查找所有基类继承子树。多重继承的派生类有可能从两个或多个基类继承同名成员,对该成员不加限定的使用是二义性的。
注意,多重继承中首先发生名字查找。你可能会感到吃惊的是,即使两个继承的同名函数有不同的形参表,也会产生错误。类似地,即使函数在一个类中是私有的而在另一个类中是公有或者受保护的,也是错误的。或者,在一个类给定义了函数,而在另一个类中没有定义,调用仍是错误的。例如:
[cpp] #include <iostream>
class Base1 {
public:
void print() {}
void display() {}
void show() {}
};
class Base2 {
public:
void print(int) {}
void show(int);
private:
void display(int) {}
};
class Derived: public Base1, public Base2 {
};
int main () {
Derived d;
d.print();
d.display();
d.show();
return 0;
}
编译结果(MinGW2.05):
Compiling source file(s)...
main.cpp
main.cpp: In function `int main()':
main.cpp:24: error: request for member `print' is ambiguous
main.cpp:13: error: candidates are: void Base2::print(int)
main.cpp:6: error: void Base1::print()
main.cpp:25: error: request for member `display' is ambiguous
main.cpp:16: error: candidates are: void Base2::display(int)
main.cpp:7: error: void Base1::display()
main.cpp:26: error: request for member `show' is ambiguous
main.cpp:14: error: candidates are: void Base2::show(int)
main.cpp:8: error: void Base1::show()
Test.exe - 9 error(s), 0 warning(s)
#include <iostream>
class Base1 {
public:
void print() {}
void display() {}
void show() {}
};
class Base2 {
public:
void print(int) {}
void show(int);
private:
void display(int) {}
};
class Derived: public Base1, public Base2 {
};
int main () {
Derived d;
d.print();
d.display();
d.show();
return 0;
}
编译结果(MinGW2.05):
Compiling source file(s)...
main.cpp
main.cpp: In function `int main()':
main.cpp:24: error: request for member `print' is ambiguous
main.cpp:13: error: candidates are: void Base2::print(int)
main.cpp:6: error: void Base1::print()
main.cpp:25: error: request for member `display' is ambiguous
main.cpp:16: error: candidates are: void Base2::display(int)
main.cpp:7: error: void Base1::display()
main.cpp:26: error: request for member `show' is ambiguous
main.cpp:14: error: candidates are: void Base2::show(int)
main.cpp:8: error: void Base1::show()
Test.exe - 9 error(s), 0 warning(s)
解决这种二义性的方法可以是通过指定使用哪个类的版本(即带上类名前缀)来解决。但最好的方法是,在解决二义性的派生类中定义函数的一个版本。
虚继承
在标准I/O库中的类都继承了一个共同的抽象基类ios,那个抽象基类管理流的条件状态并保存流所读写的缓冲区。istream和ostream类直接继承这个公共基类,库定义了另一个名为isotream的类,它同时继承istream和ostream,iostream类既可以对流进行读又可以对流进行写。如果I/O类型使用常规继承,则每个iostream对象可能包含两个ios子对象:一个包含在它的istream子对象中,另一个包含在它的 ostream子对象中。从设计角度讲,这个实现是错误的:iostream类想要对单个缓冲区进行读和写,它希望跨越输入和输出操作符共享条件状态。
在C++中,通过使用虚继承(virtual inheritance)解决这类问题。虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象。共享的基类子对象称为虚基类(virtual base class)。
通过在派生类列表中包含关键字virtual设置虚基类,例如:
[cpp]
class istream : public virtual ios {...};
class ostream : virtual public ios {...};
class iostream : public istream, public ostream {...};
class istream : public virtual ios {...};
class ostream : virtual public ios {...};
class iostream : public istream, public ostream {...};
假定通过多个派生路径继承名为X的成员,有下面三种可能性:
1)如果在每个路径中X表示同一虚基类成员,则没有二义性,因为共享该成员的单个实例;
2)如果在某个路径中X是虚基类的成员,而在另一路径中X是后代派生类的成员,也没有二义性——特定派生类实例的优先级高于共享虚基类实例。
3)如果沿每个继承路径X表示后代派生类的不同成员,则该成员的直接访问是二义性的。
例如:
[cpp]
#include <iostream>
class B {
public:
void print() {
std::cout << "B"<< std::endl;
}
};
class D1: public virtual B {
};
class D2: public virtual B {
public:
void print() {
std::cout << "D2"<< std::endl;
}
};
class DD: public D1, public D2 {
};
int main () {
DD d;
d.print(); // ok: call D2::print
return 0;
}
#include <iostream>
class B {
public:
void print() {
std::cout << "B"<< std::endl;
}
};
class D1: public virtual B {
};
class D2: public virtual B {
public:
v
补充:软件开发 , C++ ,