对象作为类中的成员变量 C++

Objects as member variables in a class in C++

本文关键字:成员 变量 C++ 对象      更新时间:2023-10-16

我想知道将对象实例作为另一个类的成员变量处理的最佳实践是什么。阅读不同的帖子后,似乎一般来说,应该避免引用对象作为成员变量,但我不确定使用指针是否是一个很好的解决方案。另一种可能性是将const引用作为类构造函数的参数,然后使用复制构造函数初始化成员变量。

  • 处理这种情况的经验法则是什么?
  • 使用share_ptr作为成员变量怎么样?
  • 如果成员变量引用抽象类怎么办?在这种情况下,是否有其他指针或引用的替代方法?

编辑:

由于问题的答案实际上取决于上下文,因此我将在类成员引用抽象类的情况下指定它。例如,请考虑以下实现策略模式的代码:

 class Client{
        // reference to Abstract strategy
        void execute(){
             strategy.doStuff();
        }
 }
 class AbstractStrategy{
 public:
      virtual void doStuff() = 0;
 }
 class ConcreteStrategy{
      void doStuff(){}
 }

由于AsbtractStrategy具有纯虚函数,因此将其作为Client成员的唯一方法似乎是使用指针或引用。有更好的解决方案吗?

(即使是原始的)指针当然没有错。你只需要负责。如果您希望它引用的对象更改,请通过指针实现成员,否则添加另一个级别的间接寻址是没有意义的,将其作为常规成员变量在内存使用和 CPU 时间方面都会更有效。

Person类应将其age成员作为数字,而不是指向数字的指针,但一个人的residence可能是一个指针,用于引用住宅集合中的对象。而且,人们并不总是需要管理住宅或使用智能指针,住宅数据层可能完全独立于人员层,与人一起删除住宅是没有意义的,相反,它应该变得空置。

这完全取决于你需要什么,对象是否需要是某种东西,拥有某种东西,或者仅仅是引用外部的东西。

请记住,间接是有代价的。这就是为什么将age成员实现为指针是没有意义的,人们的寿命不长,你可以逃脱一个字节来存储年龄,如果你有一个年龄的集合,你不会得到任何东西,你为每个人引用一个,因为指针通常是 4 或 8 个字节, 加上它引用的字节,您将浪费内存和 CPU 时间来检索该内存地址中的数据。但是住宅可能是一个大对象,加上它与人没有紧密结合,所以一个人的住所作为一个指针来实现是有意义的。虽然智能指针会在删除该人时删除该住宅,但常规指针仅允许您在该人的析构函数中从该住宅的居民引用中注销该人。

还要考虑这一点 - 你可能在对象之间有抽象,例如你可能真的不想让每个人都有一个居住指针,或者在多个住宅的情况下有多个,事实上一个人可能对住宅的存在一无所知,你仍然可以通过使用第三个对象来建立一个人和住宅之间的关系,而无需保留诸如成员, 例如,对象和居住指针的地图,因此您可以通过查询地图来获取一个人的住所(如果有),这将比为每个人设置居住指针要慢,但它将节省内存,因为不为每个人的实例存储居住指针。可能根本没有住所。

至于抽象类 - 由于它们无法实例化,因此不能将抽象类作为成员对象。由于这个想法是定义一个接口,你很可能是一个指针,并利用虚拟调度和多态性。请注意,除了聚合之外,您还可以使用继承。如果每个对象只有一个策略,并且每个对象都有它,那么聚合和继承都可以解决问题。如果某些对象具有多个对象而其他对象没有对象,则可以采用上一段中提到的解耦设计。另请注意,使用继承Strategy::doStuff()将成为该对象 vtable 的一部分,这意味着该特定类型的每个对象最终将执行相同的代码,而使用聚合或其他耦合时,可以在每个实例的基础上设置策略。

您的问题没有单一的答案。 需要考虑的因素是对象生存期。 如果对象 A 以某种形式"包含"对象 B,则对象 B 需要比对象 A 或"坏事发生"活得更长。

智能指针(shared_ptr、unique_ptr)让对象 A 强制执行在对象 A 完成之前不能删除对象的规则,因此它们是一个好主意。

将对象 B 复制到对象 A 中也为对象 B 的生存期提供了必要的保证,但您需要注意,现在有两个单独的对象 B。 这是否可以接受由您决定。

指针和引用通常是有问题的,因为 A 无法控制 B 的生命周期,但有时程序的整体结构意味着这不是问题。

摘要:了解对象生存期并了解您可以使用哪些工具。