C++链表中抽象Part类:用指针还是直接存储对象?
我们是车辆制造商,拥有如下抽象Part类:
class Part { // 注意所有方法均为const方法。 public: Part():itsPartNumber(1) {} Part(int PartNumber):itsPartNumber(PartNumber) {} virtual ~Part() {}; int GetPartNumber() const { return itsPartNumber; } virtual void Display() const = 0; private: int itsPartNumber; };
该类将作为链表的数据类,链表节点定义如下:
class PartNode { public: PartNode(Part*); ~PartNode(); void setNext(PartNode* node) { itsNext = node; } Part* GetPart() const; PartNode* GetNext() const; private: Part* itsPart; PartNode* itsNext; };
由于Part的所有方法都是const,无法对其实例进行修改操作,现提出疑问:是否可以将所有Part*改为直接使用Part类型?还是链表中的数据本身应当使用指针存储?
注:此问题并非重复提问,之前的问题讨论的是next指针,而本问题聚焦于数据存储方式。
不能直接把Part*改成Part类型,核心原因和Part是抽象类有关,和它的方法是否为const没有直接关系,具体解释如下:
抽象类无法实例化:
Part包含纯虚函数Display(),属于抽象类。C++中抽象类不能直接创建对象,若PartNode的成员变量定义为Part itsPart,编译器会直接报错——因为根本无法构造抽象类的实例。而Part*指针可以指向Part的子类实例,这正是多态的核心用法,完全匹配车辆制造商的场景:你必然会有Engine、Wheel等继承自Part的具体零件类,链表需要存储各类不同的零件实例。避免对象切片,保证多态性:即便忽略抽象类的限制,用值存储也会引发对象切片问题——将子类对象赋值给
Part类型变量时,子类特有的数据和方法会被截断,仅保留基类Part的部分,彻底违背设计抽象基类的初衷。使用指针则可以通过基类指针调用子类的Display()方法,实现正确的多态行为。内存管理与灵活性:指针存储能灵活控制对象生命周期。比如业务需要时,多个链表节点可共享同一个零件实例;也能动态创建、销毁零件对象。而值存储会让每个节点拷贝一份Part对象,既浪费内存,又因对象切片丢失信息;同时销毁节点时只能调用Part的析构函数,无法正确处理子类的资源释放(即便Part的析构是虚函数,但值存储的对象本身就是Part类型,不存在子类析构的问题,本质还是绕不开抽象类无法实例化的限制)。
总结:不管Part的方法是否为const,都必须用指针(更推荐智能指针如std::unique_ptr<Part>来避免手动内存管理)存储,因为Part是抽象类无法实例化,同时指针是实现多态的必要方式。
内容的提问来源于stack exchange,提问作者Issac Howard

