前情回顾
【C++学习记录1】讲到了继承与多态,但只是个概述,知识点不止那么一点。
- 继承:C++ 支持三种继承方式,分别是公有继承、私有继承和保护继承,用于实现类之间的继承关系和代码复用。
- 基类和派生类:在 C++ 中,派生类可以从基类继承成员变量和成员函数,并且可以添加自己的成员变量和成员函数,基类和派生类之间的关系是一种父子关系。
- 多态:在编程上,指同一种操作在不同的对象上有不同的实现方式。
什么是向上转型
向上转型指的是将一个派生类的指针或引用赋值给一个基类的指针或引用的操作。它是一种将派生类对象当做基类对象使用的方式。由于基类指针或引用可以指向派生类的对象,因此可以通过向上转型实现多态性。在向上转型时,只能访问基类中定义的成员,而不能访问派生类新增的成员或重载的成员。同时,向上转型之后的对象也无法访问派生类的私有成员
向上转型通常发生在使用多态性时,当需要将派生类对象作为基类对象来处理时,就需要进行向上转型。
下面是一个向上转型的示例代码:
class Base {
public:
virtual void func() {
cout << "Base::func()" << endl;
}
};
class Derived : public Base {
public:
virtual void func() {
cout << "Derived::func()" << endl;
}
};
int main() {
Derived d;
Base* ptr = &d; // 向上转型
ptr->func(); // 调用的是Derived类中的func()函数
return 0;
}
在上面的示例代码中,Derived类是Base类的派生类,ptr指针是Base类型的指针,通过将Derived类对象的地址赋值给ptr指针实现了向上转型。在调用ptr->func()时,由于func()函数是虚函数,所以会根据对象的实际类型调用相应的函数,因此输出的是"Derived::func()"。
向上转型的一个主要意义是可以通过基类指针或引用来操作派生类对象,从而实现多态性,提高代码的灵活性和可扩展性。
举一个比较实际的例子帮助理解
下面是一个实际例子,假设我们正在开发一个图形库,它包含多个图形对象,如矩形、圆形、三角形等等,这些图形对象都有一个共同的基类Shape,而每个具体的图形对象都有自己特有的属性和方法。为了方便管理这些图形对象,我们定义了一个包含多个Shape指针的容器类ShapeContainer,它可以添加、删除和遍历所有图形对象。
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
class Rectangle : public Shape {
public:
virtual void draw() {
cout << "Drawing a rectangle" << endl;
}
};
class Circle : public Shape {
public:
virtual void draw() {
cout << "Drawing a circle" << endl;
}
};
class ShapeContainer {
public:
void add(Shape* shape) {
shapes_.push_back(shape);
}
void remove(Shape* shape) {
shapes_.erase(remove(shapes_.begin(), shapes_.end(), shape), shapes_.end());
}
void drawAll() {
for (auto shape : shapes_) {
shape->draw(); // 调用虚函数,实现多态性
}
}
private:
vector<Shape*> shapes_;
};
在上面的代码中,Shape类是所有图形对象的基类,它定义了纯虚函数draw(),表示所有图形对象都必须实现该函数。Rectangle和Circle类分别表示矩形和圆形,它们都是Shape类的派生类,实现了draw()函数。ShapeContainer类是一个容器类,它可以添加、删除和遍历所有Shape指针,通过调用Shape指针的虚函数draw(),实现了对所有图形对象的统一绘制。当我们添加一个Rectangle或Circle对象到ShapeContainer中时,会进行向上转型,将Rectangle或Circle对象当做Shape对象来处理,这样就可以实现对所有图形对象的统一管理和操作。
通过向上转型,我们可以将派生类对象当做基类对象使用,从而将不同的派生类对象封装成一个通用的基类对象。这种方式可以提高代码的可维护性和扩展性,因为在程序需要增加新的派生类时,不需要修改原有的代码,只需要添加新的派生类和实现相应的虚函数即可。
另外,向上转型还可以使代码更加通用和灵活。例如,如果我们定义了一个基类Animal和它的派生类Dog和Cat,那么可以使用Animal*类型的指针或引用来引用Dog和Cat对象,从而实现对它们共同行为的操作,如输出它们的名字、声音等。如果没有向上转型的机制,我们将需要为每个派生类编写特定的操作函数,代码量将会大大增加。
相关知识点:
虚函数,后面讲