以下是一份关于类在继承派生中的使用教程:
一、继承和派生的概念与作用
概念:
继承是面向对象编程中的一个重要特性,它允许一个类(子类或派生类)从另一个类(父类或基类)获取属性和方法。子类可以继承父类的成员变量和成员函数,并且可以根据自身的需求进行扩展或修改。
作用:
- 代码复用:避免重复编写相同的代码,提高开发效率。例如,如果有多个类都具有一些共同的属性和行为,将这些共同部分提取到父类中,子类只需继承父类并根据需要进行个性化定制,减少了代码冗余。
- 层次结构清晰:通过继承建立起类的层次关系,使得程序的结构更加清晰、易于理解和维护。例如,在一个图形绘制系统中,可以有一个基类
Shape
,然后派生出Circle
、Rectangle
等子类,这样的层次结构能很好地组织代码逻辑。
二、继承的类型
在 C++ 中,继承主要有以下三种类型:
1. 公共继承(public
)
- 特点:父类的
public
和protected
成员在子类中保持相同的访问级别,父类的private
成员在子类中不可直接访问。 - 示例代码:
class Parent {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class Child : public Parent {
public:
void accessMembers() {
publicVar = 10; // 可以直接访问父类的 public 成员
protectedVar = 20; // 可以直接访问父类的 protected 成员
// privateVar = 30; // 错误,不能访问父类的 private 成员
}
};
- 使用场景:当希望子类完全继承父类的接口,并对外公开父类的部分接口时,通常使用公共继承。这是最常用的继承方式,符合“is-a”的关系,即子类是一种特殊的父类。
2. 保护继承(protected
)
2.1 protected
关键字
protected
关键字用于修饰类的成员,被 protected
修饰的成员在类的内部可以被访问,在子类中也可以被访问,但在类的外部不能被直接访问。
示例代码:
class Base {
protected:
int protectedData;
public:
void setProtectedData(int value) {
protectedData = value;
}
int getProtectedData() {
return protectedData;
}
};
class Derived : public Base {
public:
void modifyProtectedData() {
protectedData += 10; // 可以在子类中访问父类的 protected 成员
}
};
int main() {
Base b;
// b.protectedData = 5; // 错误,在类外部不能直接访问 protected 成员
b.setProtectedData(5);
std::cout << b.getProtectedData() << std::endl;
Derived d;
d.modifyProtectedData();
std::cout << d.getProtectedData() << std::endl;
return 0;
}
注意事项:
- 合理使用
protected
关键字可以在一定程度上控制类的成员的访问权限,既保证了子类能够继承和访问必要的实现细节,又防止了外部类的随意访问,有助于封装性的维护。但如果过度使用protected
,可能会导致类的层次结构过于紧密耦合,不利于代码的维护和扩展,因此需要谨慎权衡。
2.2 protected
继承
- 特点:父类的
public
和protected
成员在子类中都变为protected
成员,父类的private
成员在子类中仍然不可直接访问。 - 示例代码:
class Parent {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class Child : protected Parent {
public:
void accessMembers() {
publicVar = 10; // 此时父类的 public 成员在子类中变为 protected,可在子类成员函数中访问
protectedVar = 20; // 可以访问父类的 protected 成员
// privateVar = 30; // 错误,不能访问父类的 private 成员
}
};
int main() {
Child c;
// c.publicVar = 5; // 错误,在类外部不能访问 protected 成员
return 0;
}
- 使用场景:当希望子类继承父类的实现细节,但不希望外部类直接访问父类的某些接口时,可使用保护继承。这种继承方式在一定程度上隐藏了父类的接口,同时允许子类在内部使用这些接口进行扩展和修改。
3. 私有继承(private
)
- 特点:父类的
public
和protected
成员在子类中都变为private
成员,父类的private
成员在子类中同样不可直接访问。 - 示例代码:
class Parent {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class Child : private Parent {
public:
void accessMembers() {
publicVar = 10; // 此时父类的 public 成员在子类中变为 private,可在子类成员函数中访问
protectedVar = 20; // 可以访问父类的 protected 成员
// privateVar = 30; // 错误,不能访问父类的 private 成员
}
};
int main() {
Child c;
// c.publicVar = 5; // 错误,在类外部不能访问 private 成员
return 0;
}
- 使用场景:当子类只是想复用父类的部分实现代码,而不希望继承父类的接口时,可采用私有继承。私有继承通常用于实现一些特定的内部逻辑,对外界隐藏父类的存在和相关接口。
三、派生类和基类的构造函数和析构函数执行顺序
1、构造函数执行顺序
当创建派生类的对象时,构造函数的执行顺序遵循以下规则:
1). 首先执行基类的构造函数:按照继承列表中基类出现的顺序,从最顶层的基类开始依次执行其构造函数。如果存在多个基类(例如多继承的情况),先执行在继承声明中靠前位置的基类构造函数。
2). 然后执行派生类自身的构造函数:在完成所有基类构造函数的调用后,才会执行派生类自己的构造函数,用于初始化派生类特有的成员变量等操作。
示例代码(单继承情况):
#