C++ OOP 面试高频知识点 - 10
抽象类、纯虚函数、接口实现方式
1. 抽象类的基本概念
抽象类(Abstract Class) 是包含至少一个纯虚函数的类。抽象类不能直接实例化,但可以作为基类使用,用于定义接口和实现多态。
2. 纯虚函数的定义
纯虚函数是在虚函数声明后面加 = 0 的函数。
class AbstractClass {
public:
virtual void pureVirtualFunction() = 0; // 纯虚函数
};
// AbstractClass obj; // 错误:抽象类不能直接实例化
3. 抽象类的继承和实现
子类必须实现所有的纯虚函数,否则子类也会成为抽象类。
class AbstractShape {
public:
virtual double area() const = 0;
virtual double perimeter() const = 0;
};
class Rectangle : public AbstractShape {
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override {
return width * height;
}
double perimeter() const override {
return 2 * (width + height);
}
private:
double width, height;
};
class Circle : public AbstractShape {
public:
Circle(double r) : radius(r) {}
double area() const override {
return 3.14159 * radius * radius;
}
double perimeter() const override {
return 2 * 3.14159 * radius;
}
private:
double radius;
};
4. 接口的实现方式
在 C++ 中,接口通常通过抽象类实现,这些类只包含纯虚函数,没有数据成员。
class Drawable {
public:
virtual void draw() const = 0;
virtual ~Drawable() = default; // 虚析构函数
};
class Movable {
public:
virtual void move(int x, int y) = 0;
virtual ~Movable() = default;
};
class Shape : public Drawable, public Movable {
// 抽象类,因为没有实现所有纯虚函数
};
class Rectangle : public Shape {
public:
Rectangle(int x, int y, int w, int h)
: x(x), y(y), width(w), height(h) {}
void draw() const override {
cout << "Drawing rectangle at (" << x << "," << y
<< ") with size " << width << "x" << height << endl;
}
void move(int dx, int dy) override {
x += dx;
y += dy;
}
private:
int x, y;
int width, height;
};
5. 抽象类的特性
5.1 不能实例化
抽象类不能直接创建对象,但可以作为指针或引用类型。
AbstractShape* shapePtr = new Rectangle(5, 10);
AbstractShape& shapeRef = *shapePtr;
shapePtr->area();
shapeRef.area();
delete shapePtr;
5.2 虚析构函数
抽象类应该有虚析构函数,以确保通过基类指针删除子类对象时能正确调用子类的析构函数。
class AbstractClass {
public:
virtual void foo() = 0;
virtual ~AbstractClass() = default; // 虚析构函数
};
class Derived : public AbstractClass {
public:
Derived() { data = new int[100]; }
~Derived() { delete[] data; }
void foo() override {}
private:
int* data;
};
int main() {
AbstractClass* ptr = new Derived();
delete ptr; // 正确调用 Derived 的析构函数
return 0;
}
5.3 可以有非纯虚函数
抽象类可以包含非纯虚函数,这些函数可以有默认实现。
class AbstractClass {
public:
virtual void pureVirtual() = 0;
void concreteFunction() {
cout << "Concrete function implementation" << endl;
}
};
class Derived : public AbstractClass {
public:
void pureVirtual() override {
cout << "Implementation of pure virtual function" << endl;
}
};
int main() {
AbstractClass* ptr = new Derived();
ptr->pureVirtual();
ptr->concreteFunction();
delete ptr;
return 0;
}
6. 抽象类的用途
6.1 定义接口
抽象类可以用于定义接口,确保所有实现类都包含必要的方法。
class DatabaseConnection {
public:
virtual void connect() = 0;
virtual void disconnect() = 0;
virtual void executeQuery(const string& query) = 0;
};
6.2 实现多态
抽象类是实现多态的重要方式,它允许我们通过基类指针或引用处理不同类型的对象。
void drawAll(const vector<Drawable*>& objects) {
for (auto obj : objects) {
obj->draw();
}
}
int main() {
vector<Drawable*> shapes;
shapes.push_back(new Rectangle(10, 20, 5, 5));
shapes.push_back(new Circle(15, 15, 3));
drawAll(shapes);
for (auto obj : shapes) {
delete obj;
}
return 0;
}
6.3 代码复用
通过抽象类,可以实现代码复用,让多个类继承同一个基类的默认实现。
class AbstractLogger {
public:
virtual void log(const string& message) = 0;
void logError(const string& message) {
log("ERROR: " + message);
}
void logInfo(const string& message) {
log("INFO: " + message);
}
};
class ConsoleLogger : public AbstractLogger {
public:
void log(const string& message) override {
cout << message << endl;
}
};
7. 常见问题和回答
问题 1:抽象类和接口的区别?
在 C++ 中,接口通常指包含纯虚函数的抽象类,但抽象类可以包含非纯虚函数和数据成员。
- 抽象类:可以包含纯虚函数和非纯虚函数,以及数据成员
- 接口:通常只包含纯虚函数,没有数据成员,用于定义契约
问题 2:为什么要使用抽象类?
- 定义接口,确保实现类遵循契约
- 实现多态,提高代码复用性
- 隐藏实现细节,提高代码安全性
问题 3:可以有抽象类的对象数组吗?
不可以,但可以有抽象类指针或引用的数组。
AbstractShape* shapes[3];
shapes[0] = new Rectangle(1, 2);
shapes[1] = new Circle(3);
shapes[2] = new Triangle(4, 5, 6);
问题 4:纯虚函数可以有默认实现吗?
在 C++ 中,纯虚函数可以有默认实现,但需要在类外部定义。
class AbstractClass {
public:
virtual void foo() = 0;
};
void AbstractClass::foo() {
cout << "Default implementation" << endl;
}
class Derived : public AbstractClass {
public:
void foo() override {
AbstractClass::foo(); // 调用默认实现
}
};
8. 最佳实践
- 定义清晰的接口:抽象类应该只包含接口,避免包含实现细节
- 使用虚析构函数:确保正确的内存管理
- 保持简洁:抽象类应该保持简洁,只包含必要的方法
- 明确命名:接口方法应该有明确的含义,表达清晰的意图
总结
抽象类是 C++ 中实现接口和多态的重要工具。通过包含纯虚函数,抽象类定义了其他类必须遵循的契约。抽象类不能直接实例化,但可以作为基类使用,实现代码复用和多态行为。
正确使用抽象类可以提高代码的可维护性、可扩展性和安全性,使我们的程序更加模块化。
练习建议: 1. 设计一个图形系统,包含抽象类和多个具体实现 2. 实现一个日志系统,使用抽象类定义接口 3. 测试抽象类的多态行为 4. 验证虚析构函数的重要性