C++类层次结构与相关特性深度解析
立即解锁
发布时间: 2025-08-22 00:47:46 阅读量: 7 订阅数: 30 


C++编程语言精髓与实践
# C++ 类层次结构与相关特性深度解析
## 1. 引言
在 C++ 编程中,类层次结构是构建复杂软件系统的重要基础。它涉及到多个方面,如多重继承、访问控制、运行时类型信息、成员指针以及自由存储管理等。了解这些概念和技术,能够帮助开发者更高效地设计和实现功能强大的程序。
## 2. 多重继承
### 2.1 基本概念
多重继承允许一个类有多个直接基类。例如,在模拟卫星的场景中,`Satellite` 类可以同时继承 `Task` 和 `Displayed` 类:
```cpp
class Satellite : public Task, public Displayed {
// ...
};
```
与单继承相比,多重继承提供了更大的灵活性。在单继承中,`Satellite` 只能是 `Task` 或 `Displayed` 中的一个,而多重继承允许它同时具备两者的特性。
### 2.2 操作应用
`Satellite` 类不仅可以使用自身定义的操作,还可以应用 `Task` 和 `Displayed` 类的操作:
```cpp
void f(Satellite& s) {
s.draw(); // Displayed::draw()
s.delay(10); // Task::delay()
s.transmit(); // Satellite::transmit()
}
```
同时,`Satellite` 对象可以传递给期望 `Task` 或 `Displayed` 对象的函数:
```cpp
void highlight(Displayed*);
void suspend(Task*);
void g(Satellite* p) {
highlight(p); // pass a pointer to the Displayed part of the Satellite
suspend(p); // pass a pointer to the Task part of the Satellite
}
```
### 2.3 歧义解决
当两个基类有同名成员函数时,需要进行歧义解决。例如,`Task` 和 `Displayed` 类都有 `get_debug()` 函数:
```cpp
class Task {
// ...
virtual debug_info* get_debug();
};
class Displayed {
// ...
virtual debug_info* get_debug();
};
```
在使用 `Satellite` 时,需要明确指定调用哪个基类的函数:
```cpp
void f(Satellite* sp) {
debug_info* dip = sp->get_debug(); // error: ambiguous
dip = sp->Task::get_debug(); // ok
dip = sp->Displayed::get_debug(); // ok
}
```
更好的做法是在派生类中定义一个新函数来解决歧义:
```cpp
class Satellite : public Task, public Displayed {
// ...
debug_info* get_debug() {
debug_info* dip1 = Task::get_debug();
debug_info* dip2 = Displayed::get_debug();
return dip1->merge(dip2);
}
};
```
### 2.4 继承与 using 声明
不同基类的成员函数可能会出现名称冲突,且重载解析不会跨不同类作用域。例如:
```cpp
class Task {
// ...
void debug(double p);
};
class Displayed {
// ...
void debug(int v);
};
class Satellite : public Task, public Displayed {
// ...
};
void g(Satellite* p) {
p->debug(1); // error: ambiguous. Displayed::debug(int) or Task::debug(double) ?
p->Task::debug(1); // ok
p->Displayed::debug(1); // ok
}
```
如果希望根据参数类型进行选择,可以使用 `using` 声明:
```cpp
class A {
public:
int f(int);
char f(char);
// ...
};
class B {
public:
double f(double);
// ...
};
class AB : public A, public B {
public:
using A::f;
using B::f;
char f(char); // hides A::f(char)
AB f(AB);
};
void g(AB& ab) {
ab.f(1); // A::f(int)
ab.f('a'); // AB::f(char)
ab.f(2.0); // B::f(double)
ab.f(ab); // AB::f(AB)
}
```
### 2.5 复制基类
当一个类作为基类出现多次时,会有复制基类的情况。例如,`Task` 和 `Displayed` 都继承自 `Link` 类,`Satellite` 会有两个 `Link` 对象:
```cpp
struct Link {
Link* next;
};
class Task : public Link {
// ...
};
class Displayed : public Link {
// ...
};
```
这通常不会引起问题,但在引用 `Link` 类的成员时需要注意歧义:
```cpp
void mess_with_links(Satellite* p) {
p->next = 0; // error: ambiguous (which Link?)
p->Link::next = 0; // error: ambiguous (which Link?)
p->Task::next = 0; // ok
p->Displayed::next = 0; // ok
}
```
### 2.6 虚基类
在某些情况下,复制基类可能会导致问题。例如,当 `Storable` 类用于存储对象的文件名时,为了避免存储多个对象副本,需要使用虚基类:
```cpp
class Storable {
public:
Storable(const char* s);
virtual void read() = 0;
virtual void write() = 0;
virtual ~Storable();
private:
const char* store;
Storable(const Storable&);
Storable& operator=(const Storable&);
};
class Transmitter : public virtual Storable {
public:
void write();
// ...
};
class Receiver : public virtual Storable {
public:
void write();
// ...
};
class Radio : public Transmitter, public Receiver {
public:
void write();
// ...
};
```
虚基类的所有派生类共享同一个对象。
### 2.7 虚基类编程
在定义带有虚基类的类的函数时,程序员通常不知道基类是否会与其他派生类共享。例如,虚基类的构造函数由最派生类的构造函数调用,且只调用一次:
```cpp
class A {
// ...
};
class B {
public:
B();
// ...
};
class C {
public:
C(int);
};
class D : virtual public A, virtual public B, virtual public C {
D() { /* ... */ } // error: no default constructor for C
D(int i) : C(i) { /* ... */ }; // ok
// ...
};
```
### 2.8 多重继承的使用
多重继承有多种应用场景。一种简单的用法是将两个无关的类组合成一个新类,如 `Satellite` 类的例子。另一种更重要的用法是为抽象类提供实现,例如 `BBival slider` 类:
```cpp
class BBival slider
: public Ival slider
, protected BBslider
{
// ...
};
```
此外,多重继承还允许兄弟类共享信息,避免依赖唯一的公共基类。在菱形继承的情况下,如果基类不能复制,则需要使用虚基类。
### 2.9 虚基类函数的覆盖
派生类可以覆盖其直接或间接虚基类的虚函数。不同的派生类可以覆盖虚基类的不同虚函数,多个派生类可以为虚基类的接口提供实现。例如:
```cpp
class Window {
// ...
virtual void set_color(Color) = 0;
virtual void prompt() = 0;
};
class Window with border : public virtual Window {
// ...
void set_color(Color);
};
class Window with menu : public virtual Window {
// ...
void prompt();
};
class My window : public Window with menu, public Window with border {
// ...
void prompt();
};
```
如果不同的派生类覆盖了同一个函数,必须有一个函数覆盖其他所有函数,否则会导致类层次结构错误。
## 3. 访问控制
### 3.1 成员访问权限
类的成员可以是 `private`、`protected` 或 `public`:
- `private`:只能由类的成员函数和友元使用。
- `protected`:可以由类的成员函数、友元以及派生类的成员函数和友元使用。
- `public`:可以由任何函数使用。
例如,在一个列表类中,一些成员可以设置为 `private` 以隐藏实现细节:
```cpp
template<class T> class List {
private:
struct Link { T val; Link* next; };
struct Chunk {
enum { chunk_size = 15 };
Link v[chunk_size];
Chunk* next;
};
Chunk* allocated;
Link* free;
Link* get_free();
Link* head;
public:
class Underflow { }; // exception class
void insert(T);
T get();
// ...
};
```
### 3.2 受保护成员
受保护成员可以在派生类中使用,但只能用于自身类型的对象。例如:
```cpp
class Buffer {
protected:
char a[128];
// ...
};
class Linked buffer : public Buffer { /* ... */ };
class Cyclic buffer : public Buffer {
// ...
void f(Linked buffer* p) {
a[0] = 0; // ok: access to cyclic buffer’s own protected member
p->a[0] = 0; // error: access to protected member of different type
}
};
```
使用受保护成员时需要谨慎,尤其是数据成员,因为它们容易被滥用,导致数据损坏和维护困难。
### 3.3 基类访问权限
基类也可以声明为 `private`、`protected` 或 `public`:
- `public` 派生:派生类是基类的子类型。
- `protected` 派生:用于表示实现细节,适用于需要进一步派生的类层次结构。
- `private` 派生:用于限制基类的接口,提供更强的保证。
例如:
```cpp
class X : public B { /* ... */ };
class Y : protected B { /* ... */ };
class Z : private B { /* ... */ };
```
0
0
复制全文
相关推荐










