C++ 基类与派生类
引言
面向对象编程的三大核心特性是封装、继承和多态。其中继承是实现代码重用和构建类层次结构的重要机制。在C++中,通过基类(父类)和派生类(子类)的概念来实现继承关系,这使得我们可以创建一个从一般到特殊的类层次结构,增强代码的可维护性和可扩展性。
本文将详细介绍C++中的基类和派生类的概念、语法以及使用方法,帮助你掌握这一重要的面向对象编程特性。
基类与派生类的基本概念
什么是基类和派生类?
- 基类(Base Class):也称为父类或超类,是被继承的类。基类包含了可以被其他类继承的属性和方法。
- 派生类(Derived Class):也称为子类,是从基类继承属性和方法的类。派生类不仅继承了基类的特性,还可以添加自己的特性或修改继承的特性。
继承的语法
在C++中,定义派生类的基本语法如下:
class 基类名 {
// 基类成员...
};
class 派生类名 : 继承方式 基类名 {
// 派生类成员...
};
其中,继承方式可以是:
public
:基类的公有成员成为派生类的公有成员,保护成员保持保护属性protected
:基类的公有和保护成员都成为派生类的保护成员private
:基类的公有和保护成员都成为派生类的私有成员
继承方式与访问控制
公有继承(Public Inheritance)
公有继承是最常用的继承方式,表示"是一种"(is-a)关系。
#include <iostream>
using namespace std;
class Animal {
public:
void eat() {
cout << "动物在进食" << endl;
}
protected:
int age;
private:
string name;
};
class Dog : public Animal {
public:
void bark() {
cout << "狗在汪汪叫" << endl;
cout << "年龄: " << age << endl; // 可以访问基类的protected成员
// cout << name << endl; // 错误:不能访问基类的private成员
}
};
int main() {
Dog myDog;
myDog.eat(); // 可以访问继承自基类的公有方法
myDog.bark(); // 可以访问派生类自己的方法
// myDog.age = 5; // 错误:不能从外部访问protected成员
return 0;
}
输出:
动物在进食
狗在汪汪叫
年龄: 0
保护继承(Protected Inheritance)
保护继承使基类的公有成员在派生类中变为保护成员,这意味着它们不能被派生类的对象直接访问。
class Bird : protected Animal {
public:
void fly() {
cout << "鸟在飞翔" << endl;
eat(); // 可以访问继承自基类的方法(现在是protected)
}
};
int main() {
Bird myBird;
// myBird.eat(); // 错误:基类的public方法在派生类中变为protected
myBird.fly(); // 正确
return 0;
}
私有继承(Private Inheritance)
私有继承使基类的所有成员在派生类中都变为私有成员。
class Fish : private Animal {
public:
void swim() {
cout << "鱼在游泳" << endl;
eat(); // 可以访问继承自基类的方法(现在是private)
}
};
int main() {
Fish myFish;
// myFish.eat(); // 错误:基类的public方法在派生类中变为private
myFish.swim(); // 正确
return 0;
}
大多数情况下,我们会使用公有继承,因为它最符合面向对象设计中的"是一种"关系。保护继承和私有继承在特定场景下有用,但使用频率较低。
构造函数和析构函数
派生类对象的构造与析构顺序
当创建一个派生类对象时:
- 先调用基类的构造函数
- 然后调用派生类的构造函数
当销毁一个派生类对象时:
- 先调用派生类的析构函数
- 然后调用基类的析构函数
这个顺序与构造顺序相反。
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "基类构造函数" << endl;
}
~Base() {
cout << "基类析构函数" << endl;
}
};
class Derived : public Base {
public:
Derived() {
cout << "派生类构造函数" << endl;
}
~Derived() {
cout << "派生类析构函数" << endl;
}
};
int main() {
cout << "创建派生类对象..." << endl;
Derived d;
cout << "程序结束..." << endl;
return 0;
}
输出:
创建派生类对象...
基类构造函数
派生类构造函数
程序结束...
派生类析构函数
基类析构函数
派生类构造函数初始化列表
派生类可以通过初始化列表来调用基类的特定构造函数:
#include <iostream>
using namespace std;
class Person {
protected:
string name;
int age;
public:
Person(string n, int a) : name(n), age(a) {
cout << "Person构造函数" << endl;
}
void introduce() {
cout << "我是 " << name << ",今年 " << age << " 岁。" << endl;
}
};
class Student : public Person {
private:
string school;
public:
// 通过初始化列表调用基类构造函数
Student(string n, int a, string s) : Person(n, a), school(s) {
cout << "Student构造函数" << endl;
}
void study() {
cout << name << " 在 " << school << " 学习" << endl;
}
};
int main() {
Student s("小明", 15, "第一中学");
s.introduce();
s.study();
return 0;
}
输出:
Person构造函数
Student构造函数
我是 小明,今年 15 岁。
小明 在 第一中学 学习
继承中的方法覆盖(重写)
派生类可以重写(override)基类中的方法,提供特定于派生类的实现。
#include <iostream>
using namespace std;
class Shape {
public:
void draw() {
cout << "绘制形状" << endl;
}
};
class Circle : public Shape {
public:
// 重写基类的draw方法
void draw() {
cout << "绘制圆形" << endl;
}
};
class Rectangle : public Shape {
public:
// 重写基类的draw方法
void draw() {
cout << "绘制矩形" << endl;
}
};
int main() {
Shape shape;
Circle circle;
Rectangle rectangle;
shape.draw(); // 调用Shape::draw()
circle.draw(); // 调用Circle::draw()
rectangle.draw(); // 调用Rectangle::draw()
return 0;
}
输出:
绘制形状
绘制圆形
绘制矩形
在C++中,普通方法的覆盖并不需要特殊的关键字。但为了代码清晰,C++11引入了override
关键字,可以明确表示一个方法是对基类方法的重写,如果不是重写会导致编译错误。
class Circle : public Shape {
public:
void draw() override { // 使用override关键字
cout << "绘制圆形" << endl;
}
};
基类指针和引用
C++中,可以使用基类的指针或引用来引用派生类对象,这是实现多态的基础。
#include <iostream>
using namespace std;
class Animal {
public:
void speak() {
cout << "动物在发声" << endl;
}
};
class Dog : public Animal {
public:
void speak() {
cout << "汪汪!" << endl;
}
};
class Cat : public Animal {
public:
void speak() {
cout << "喵喵!" << endl;
}
};
int main() {
Dog dog;
Cat cat;
// 基类引用引用派生类对象
Animal& animalRef1 = dog;
Animal& animalRef2 = cat;
// 基类指针指向派生类对象
Animal* animalPtr1 = &dog;
Animal* animalPtr2 = &cat;
// 使用基类引用调用方法
animalRef1.speak(); // 动物在发声(非虚函数)
animalRef2.speak(); // 动物在发声(非虚函数)
// 使用基类指针调用方法
animalPtr1->speak(); // 动物在发声(非虚函数)
animalPtr2->speak(); // 动物在发声(非虚函数)
return 0;
}
输出:
动物在发声
动物在发声
动物在发声
动物在发声
注意,上面的例子中没有实现多态,因为speak()
不是虚函数。要实现多态,需要在基类方法前加上virtual
关键字。这部分内容将在"C++虚函数与多态"章节详细介绍。
实际应用案例:图形绘制系统
下面是一个简单的图形绘制系统示例,展示了基类和派生类在实际应用中的作用:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 基类:图形
class Shape {
protected:
string color;
public:
Shape(string c) : color(c) {}
// 设置颜色
void setColor(string c) {
color = c;
}
// 获取颜色
string getColor() {
return color;
}
// 计算面积的虚函数(将在派生类中实现)
virtual double area() = 0;
// 绘制图形的虚函数(将在派生类中实现)
virtual void draw() = 0;
};
// 派生类:圆形
class Circle : public Shape {
private:
double radius;
public:
Circle(double r, string c) : Shape(c), radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
void draw() override {
cout << "绘制" << color << "圆形,半径为" << radius << endl;
}
};
// 派生类:矩形
class Rectangle : public Shape {
private:
double width;
double height;
public:
Rectangle(double w, double h, string c) : Shape(c), width(w), height(h) {}
double area() override {
return width * height;
}
void draw() override {
cout << "绘制" << color << "矩形,宽为" << width << ",高为" << height << endl;
}
};
// 派生类:三角形
class Triangle : public Shape {
private:
double base;
double height;
public:
Triangle(double b, double h, string c) : Shape(c), base(b), height(h) {}
double area() override {
return 0.5 * base * height;
}
void draw() override {
cout << "绘制" << color << "三角形,底为" << base << ",高为" << height << endl;
}
};
int main() {
// 创建不同类型的图形
Circle circle(5.0, "红色");
Rectangle rectangle(4.0, 6.0, "蓝色");
Triangle triangle(3.0, 4.0, "绿色");
// 使用容器存储不同类型的图形(使用基类指针)
vector<Shape*> shapes;
shapes.push_back(&circle);
shapes.push_back(&rectangle);
shapes.push_back(&triangle);
// 绘制所有图形并计算面积
cout << "图形绘制:" << endl;
for (auto shape : shapes) {
shape->draw();
cout << "面积: " << shape->area() << endl;
cout << endl;
}
return 0;
}
输出:
图形绘制:
绘制红色圆形,半径为5
面积: 78.5398
绘制蓝色矩形,宽为4,高为6
面积: 24
绘制绿色三角形,底为3,高为4
面积: 6
这个例子展示了:
- 使用抽象基类定义共同接口
- 派生类实现特定功能
- 使用基类指针实现多态
- 通过继承实现代码重用
总结
本文详细介绍了C++中基类和派生类的概念及其实现。主要内容包括:
- 基类和派生类的基本概念和语法
- 三种继承方式及其访问控制
- 构造函数和析构函数的调用顺序
- 派生类中方法的覆盖
- 基类指针和引用的使用
- 实际应用案例
通过继承机制,我们可以建立类的层次结构,实现代码重用,为多态提供基础。掌握好基类和派生类的使用,是面向对象编程的重要一步。
练习
- 创建一个
Vehicle
基类,包含属性如brand
和year
,以及方法如start()
和stop()
。然后创建派生类Car
和Motorcycle
,添加特定属性和方法。 - 实现一个员工管理系统,创建
Employee
基类,然后派生出Manager
、Engineer
和Salesperson
类。每个类都应有适当的属性和方法。 - 修改上面的图形绘制系统,添加一个新的图形类型
Ellipse
(椭圆)。
进一步学习资源
- 《C++ Primer》中关于继承的章节
- 《Effective C++》中关于继承与面向对象设计的条款
- 在线C++参考: cppreference.com中的继承相关内容
继承是强大的工具,但不应过度使用。请记住"组合优于继承"的设计原则,在适当的场景选择继承或组合。