c++子类重写父类的方法 父类方法必须使用虚函数吗?
C++ 子类重写父类的方法
在 C++ 中,子类重写父类的方法并不一定需要父类方法声明为虚函数,但如果您希望实现多态性,确实需要将父类的方法声明为虚函数。以下是详细说明:
1. 父类方法是否必须声明为虚函数?
- 不是必须的:子类可以重写父类的方法,即使父类的方法没有被声明为虚函数。但是,这样做并不会实现多态性。
- 多态性:如果您希望通过基类指针或引用来调用派生类中的重写方法,父类的相应方法必须声明为虚函数。
2. 子类重写是否必须加 override?
- 不是必须的:在子类中重写父类的方法时,并不强制要求使用
override关键字。没有override也可以正常重写方法。 - 建议使用
override:使用override是一种良好的编程习惯,它可以让编译器检查您是否正确重写了父类的方法。如果方法签名不匹配,编译器会提示错误,从而减少潜在的 bug。
示例代码
以下是一个简单的示例,展示了这两者的区别:
#include <iostream>
class Base {
public:
void show() { // 非虚函数
std::cout << "Base class show function" << std::endl;
}
};
class Derived : public Base {
public:
void show() { // 重写,但不使用 override
std::cout << "Derived class show function" << std::endl;
}
};
class AnotherBase {
public:
virtual void display() { // 虚函数
std::cout << "AnotherBase class display function" << std::endl;
}
};
class AnotherDerived : public AnotherBase {
public:
void display() override { // 重写,并使用 override
std::cout << "AnotherDerived class display function" << std::endl;
}
};
int main() {
Base* b = new Derived();
b->show(); // 调用 Base::show()
AnotherBase* ab = new AnotherDerived();
ab->display(); // 调用 AnotherDerived::display()
delete b;
delete ab;
return 0;
}
抽象类和纯虚函数
在 C++ 中,抽象类和纯虚函数是面向对象编程的重要概念。它们用于定义接口并强制派生类实现特定的功能。以下是对这两个概念的详细说明:
抽象类
- 定义:抽象类是不能被实例化的类,通常包含一个或多个纯虚函数。它的主要目的是提供一个接口,供派生类实现。
- 特征:
- 抽象类可以包含普通成员函数和数据成员。
- 任何包含至少一个纯虚函数的类都被视为抽象类。
- 抽象类可以有构造函数和析构函数。
纯虚函数
- 定义:纯虚函数是一种没有实现的虚函数,必须在派生类中被重写。它通过在函数声明末尾添加
= 0来定义。 - 语法:
class AbstractClass { public: virtual void pureVirtualFunction() = 0; // 纯虚函数 }; - 代码实例:
#include<iostream>
using namespace std;
// 抽象类 Teacher
class Teacher{
public:
string name;
string shool;
string major;
virtual void goInClass() = 0; // 纯虚函数
virtual void startTeaching() = 0; // 纯虚函数
virtual void afterTeaching() = 0; // 纯虚函数
};
// 抽象类 EnglishTeacher 继承自 Teacher
class EnglishTeacher : public Teacher{
public:
void goInClass() override{ // 重写父类的纯虚函数
cout << "英语老师开始进入教室" << endl;
}
void startTeaching() override{ // 重写父类的纯虚函数
cout << "英语老师开始授课" << endl;
}
void afterTeaching() override{ // 重写父类的纯虚函数
cout<<"英语老师下课!"<<endl;
}
};
// 抽象类 ProTeacher 继承自 Teacher
class ProTeacher : public Teacher{
public:
void goInClass() override{ // 重写父类的纯虚函数
cout << "专业老师开始进入教室" << endl;
}
void startTeaching() override{ // 重写父类的纯虚函数
cout << "专业老师开始授课,并播放PPT" << endl;
}
void afterTeaching() override{ // 重写父类的纯虚函数
cout<<"编程老师下课手把手教你敲代码"<<endl;
}
};
int main()
{
// Teacher t;// 抽象类, 不支持实例化 子类必须实现抽象类的所有方法
EnglishTeacher e;
e.goInClass();
ProTeacher t;
t.startTeaching();
t.afterTeaching();
// 抽象类, 多态
Teacher *teacher = new ProTeacher;
teacher->startTeaching();
return 0;
}
- 代码运行结果
抽象类总结
定义
- 抽象类是不能被实例化的类。
- 抽象类通常包含一个或多个纯虚函数。
特点
- 抽象类可以包含普通的成员函数和数据成员。
- 任何包含至少一个纯虚函数的类都被视为抽象类。
- 抽象类可以有构造函数和析构函数。
作用
- 抽象类主要用于定义一个通用的接口或协议。
- 它为派生类提供了一个基础,派生类必须实现抽象类中的纯虚函数。
纯虚函数
- 纯虚函数是没有实现的虚函数,必须在派生类中被重写。
- 通过在函数声明末尾添加
= 0来定义纯虚函数。
多态
- 通过基类指针或引用,可以调用派生类中重写的虚函数。
- 这就实现了运行时多态,提高了代码的灵活性和可扩展性。
总之,抽象类是面向对象编程中的一个重要概念,它可以帮助我们定义通用接口,实现多态,并构建更加灵活和可扩展的软件系统。
虚函数与纯虚函数的区别
虚函数
-
定义:
- 虚函数是在基类中声明的函数,用于实现多态。
- 使用关键字
virtual声明。
-
实现:
- 虚函数可以在基类中有实现,也可以在派生类中重写。
- 如果派生类没有重写,基类的实现会被使用。
-
实例化:
- 基类可以被实例化,即使它包含虚函数。
-
用途:
- 主要用于提供多态性,使得可以通过基类指针或引用调用派生类中的重写方法。
纯虚函数
-
定义:
- 纯虚函数是没有实现的虚函数,声明时以
= 0结尾。 - 也使用关键字
virtual声明。
- 纯虚函数是没有实现的虚函数,声明时以
-
实现:
- 纯虚函数在基类中没有实现,必须在派生类中被重写。
- 如果派生类没有重写纯虚函数,该派生类仍然是抽象类,不能实例化。
-
实例化:
- 基类包含纯虚函数时,不能被实例化,称为抽象类。
-
用途:
- 用于定义一个接口或协议,强制派生类实现特定的功能。
总结
- 虚函数:可以有实现,允许基类被实例化,提供多态性。
- 纯虚函数:没有实现,定义为抽象类,强制派生类实现,主要用于接口定义。
接口相关
定义
- 接口是一种抽象的类型,用于定义一组方法的集合,描述对象应具备的行为。
- 在 C++ 中,接口通常通过抽象类实现,包含一个或多个纯虚函数。
主要特点
- 无实现: 接口仅提供方法的声明,不包含任何实现细节。
- 多态性: 接口支持多态性,使得不同的类可以实现相同的接口,从而可以通过基类指针或引用调用这些类的方法。
#include <iostream>
#include <cmath>
using namespace std;
// 定义接口 Shape
class Shape {
public:
// 纯虚函数,计算面积
virtual double area() = 0;
// 纯虚函数,计算周长
virtual double perimeter() = 0;
};
// 实现 Circle 类
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return M_PI * radius * radius; // πr²
}
double perimeter() override {
return 2 * M_PI * radius; // 2πr
}
};
// 实现 Rectangle 类
class Rectangle : public Shape {
private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() override {
return width * height; // w * h
}
double perimeter() override {
return 2 * (width + height); // 2(w + h)
}
};
// 主函数
int main() {
Shape* circle = new Circle(5.0);
Shape* rectangle = new Rectangle(4.0, 6.0);
cout << "Circle Area: " << circle->area() << endl;
cout << "Circle Perimeter: " << circle->perimeter() << endl;
cout << "Rectangle Area: " << rectangle->area() << endl;
cout << "Rectangle Perimeter: " << rectangle->perimeter() << endl;
// 清理内存
delete circle;
delete rectangle;
return 0;
}
- 运行结果