C++继承机制深度解析
立即解锁
发布时间: 2025-08-19 01:36:32 阅读量: 1 订阅数: 6 


C++面向对象编程入门与实践
### C++ 继承机制深度解析
#### 1. 访问说明符与继承
在 C++ 中,访问说明符(access specifiers)在类的设计和使用中起着关键作用,尤其是在涉及继承时。访问说明符主要有三种:`public`、`protected` 和 `private`。它们决定了类的成员(数据和函数)可以被哪些部分访问。
| 访问说明符 | 自身类可访问 | 派生类可访问 | 类外部对象可访问 |
| ---- | ---- | ---- | ---- |
| public | 是 | 是 | 是 |
| protected | 是 | 是 | 否 |
| private | 是 | 否 | 否 |
如果编写的类可能在未来作为其他类的基类,那么派生类可能需要访问的成员数据应设为 `protected` 而非 `private`,这样能确保类具备“可继承性”。不过,将类成员设为 `protected` 也有弊端。若发布一个类库,购买者可通过派生新类访问这些 `protected` 成员,其安全性远不如 `private` 成员。为避免数据损坏,通常让派生类仅通过基类的 `public` 函数访问数据更安全。
无论是否有派生类,基类本身不会改变。并且,继承是单向的,基类及其对象对派生类一无所知。例如,基类 `Counter` 的对象不能使用派生类 `CountDn` 中的 `operator--()` 函数。
在某些语言中,基类也被称为超类(superclass),派生类被称为子类(subclass);还有人将基类称为父类,派生类称为子类。
#### 2. 派生类构造函数
在 `COUNTEN` 程序中,若要初始化 `CountDn` 对象,不能直接使用 `Counter` 类的单参数构造函数。编译器只会用基类的无参数构造函数,对于更复杂的构造函数则不适用。因此,需要为派生类编写新的构造函数,如 `COUNTEN2` 程序所示:
```cpp
// counten2.cpp
// constructors in derived class
#include <iostream>
using namespace std;
class Counter
{
protected:
unsigned int count;
public:
Counter() : count()
{ }
Counter(int c) : count(c)
{ }
unsigned int get_count() const
{ return count; }
Counter operator ++ ()
{ return Counter(++count); }
};
class CountDn : public Counter
{
public:
CountDn() : Counter()
{ }
CountDn(int c) : Counter(c)
{ }
CountDn operator -- ()
{ return CountDn(--count); }
};
int main()
{
CountDn c1;
CountDn c2(100);
cout << "\nc1=" << c1.get_count();
cout << "\nc2=" << c2.get_count();
++c1; ++c1; ++c1;
cout << "\nc1=" << c1.get_count();
--c2; --c2;
cout << "\nc2=" << c2.get_count();
CountDn c3 = --c2;
cout << "\nc3=" << c3.get_count();
cout << endl;
return 0;
}
```
`CountDn` 类有两个新构造函数。无参数构造函数 `CountDn() : Counter()` 会调用基类 `Counter` 的无参数构造函数。在 `main` 函数中创建 `CountDn` 对象时,编译器先创建对象,再调用 `CountDn` 构造函数,该构造函数又会调用 `Counter` 构造函数完成初始化。单参数构造函数 `CountDn(int c) : Counter(c)` 会将参数 `c` 传递给基类的单参数构造函数。
```mermaid
graph TD;
A[创建CountDn对象] --> B[调用CountDn构造函数];
B --> C[调用Counter构造函数];
C --> D[完成对象初始化];
```
#### 3. 覆盖成员函数
在派生类中可以使用与基类同名的成员函数,即覆盖(override)基类函数。这样做可使程序对基类和派生类对象的调用方式一致。
以 `STAKEN` 程序为例,原 `Stack` 类在处理栈操作时可能存在问题,如栈满时继续入栈或栈空时出栈。为解决这些问题,派生了 `Stack2` 类:
```cpp
// staken.cpp
// overloading functions in base and derived classes
#include <iostream>
using namespace std;
#include <process.h>
class Stack
{
protected:
enum { MAX = 3 };
int st[MAX];
int top;
public:
Stack()
{ top = -1; }
void push(int var)
{ st[++top] = var; }
int pop()
{ return st[top--]; }
};
class Stack2 : public Stack
{
public:
void push(int var)
{
if(top >= MAX-1)
{ cout << "\nError: stack is full"; exit(1); }
Stack::push(var);
}
int pop()
{
if(top < 0)
{ cout << "\nError: stack is empty\n"; exit(1); }
return Stack::pop();
}
};
int main()
{
Stack2 s1;
s1.push(11);
s1.push(22);
s1.push(33);
cout << endl << s1.pop();
cout << endl << s1.pop();
cout << endl << s1.pop();
cout << endl << s1.pop();
cout << endl;
return 0;
}
```
`Stack2` 类中的 `push()` 和 `pop()` 函数与 `Stack` 类中的同名函数具有相同的参数和返回类型。当调用这些函数时,若对象是 `Stack2` 类型,会执行 `Stack2` 类中的函数。`Stack2` 类的 `push()` 函数会先检查
0
0
复制全文
相关推荐










