对象作为 C++ 中 class 中的成员变量
Objects as member variables in a class in C++
我想知道将对象实例作为另一个 class 的成员变量处理的最佳实践是什么。
在阅读了不同的帖子后,似乎一般来说,应该避免将对象引用作为成员变量,但我不确定使用指针是否是一个好的解决方案。另一种可能性是将 const
引用作为 class 构造函数的参数,然后使用复制构造函数来初始化成员变量。
- 处理这种情况的经验法则是什么?
- 使用
share_ptr
作为成员变量怎么样?
- 如果成员变量引用一个抽象的class怎么办?在这种情况下,是否还有其他替代指针或引用的方法?
编辑:
由于问题的答案实际上取决于上下文,所以我将在 class 成员引用抽象 class 的情况下指定它。例如,考虑以下实现策略模式的代码:
class Client{
// reference to Abstract strategy
void execute(){
strategy.doStuff();
}
}
class AbstractStrategy{
public:
virtual void doStuff() = 0;
}
class ConcreteStrategy{
void doStuff(){}
}
由于 AsbtractStrategy
具有纯虚函数,似乎将其作为 Client
成员的唯一方法是使用指针或引用。有更好的解决方案吗?
您的问题没有单一的答案。您需要考虑的因素是对象生命周期。如果对象 A "contains" 某种形式的对象 B,那么对象 B 需要比对象 A 或 "Bad Things Happen".
活得更久
智能指针(shared_ptr、unique_ptr)让对象 A 强制执行对象 B 不能删除的规则,直到对象 A 完成它,所以它们是个好主意。
将对象B复制到对象A中也为对象B的生命周期提供了必要的保证,但你需要知道现在有两个独立的对象B。这是否可以接受由您决定。
指针和引用通常是有问题的,因为 A 无法控制 B 的生命周期,但有时程序的整体结构意味着这不是问题。
总结:了解您的对象生命周期并知道您可以使用哪些工具。
(甚至是原始的)指针肯定没有错。你只需要负责。如果您希望它引用的对象发生变化,请通过指针实现该成员,否则添加另一个间接级别是没有意义的,它在内存使用和 CPU 时间方面都会更有效率常规成员变量。
A Person
class 应该有它的 age
成员作为一个数字,而不是作为一个指针,但是一个人的 residence
可能是一个指针,到引用住宅集合中的对象。而且并不总是需要人管理住所或使用智能指针,住所数据层可能完全独立于人层,将住所与人一起删除意义不大,而应该刚空出来。
这完全取决于您需要什么,对象是否需要是某物、拥有某物或仅引用外部某物。
请记住,间接访问是有代价的。这就是为什么将 age
成员实现为指针毫无意义的原因,人的寿命不会很长,你可以用一个字节来存储年龄,如果你有,你将一无所获一个年龄的集合,你为每个人引用一个,因为指针通常是 4 或 8 个字节,加上它引用的字节,你浪费内存和 CPU 时间来检索该内存地址中的数据。但是住宅可能是一个大对象,而且它与人的耦合并不紧密,因此将一个人的住宅实现为一个指针是有意义的。虽然智能指针会在删除人员时删除住宅,但常规指针只允许您在人员的析构函数中从住宅的居民引用中注销该人员。
还要考虑这个——你可能在对象之间有抽象,例如你可能真的不想让每个人都有一个住所指针,或者在多个住所的情况下有多个,事实上一个人可能对住所的存在,您仍然可以通过使用第三方对象建立人与住所之间的关系而无需保留成员,例如对象和住所指针的地图,因此您可以获得一个人的住所,如果any 通过查询地图,它比为每个人都有一个住所指针要慢,但它会节省内存,而不是为每个人实例存储住所指针。可能根本没有住所。
至于抽象 classes - 因为它们不能被实例化,所以你不能将它们作为成员对象。因为这个想法是定义一个接口,你很可能是一个指针并利用虚拟分派和多态性。请注意,除了聚合之外,您还可以使用继承。如果每个对象只有一个策略,并且每个对象都有它,那么聚合和继承都可以解决问题。在某些对象有多个而其他对象有 none 的情况下,您可以采用上一段中提到的解耦设计。另请注意,使用继承 Strategy::doStuff()
将成为该对象的 vtable 的一部分,这意味着该特定类型的每个对象最终将执行相同的代码,而使用聚合或其他耦合时,该策略可以设置为每个实例库。
我想知道将对象实例作为另一个 class 的成员变量处理的最佳实践是什么。
在阅读了不同的帖子后,似乎一般来说,应该避免将对象引用作为成员变量,但我不确定使用指针是否是一个好的解决方案。另一种可能性是将 const
引用作为 class 构造函数的参数,然后使用复制构造函数来初始化成员变量。
- 处理这种情况的经验法则是什么?
- 使用
share_ptr
作为成员变量怎么样? - 如果成员变量引用一个抽象的class怎么办?在这种情况下,是否还有其他替代指针或引用的方法?
编辑:
由于问题的答案实际上取决于上下文,所以我将在 class 成员引用抽象 class 的情况下指定它。例如,考虑以下实现策略模式的代码:
class Client{
// reference to Abstract strategy
void execute(){
strategy.doStuff();
}
}
class AbstractStrategy{
public:
virtual void doStuff() = 0;
}
class ConcreteStrategy{
void doStuff(){}
}
由于 AsbtractStrategy
具有纯虚函数,似乎将其作为 Client
成员的唯一方法是使用指针或引用。有更好的解决方案吗?
您的问题没有单一的答案。您需要考虑的因素是对象生命周期。如果对象 A "contains" 某种形式的对象 B,那么对象 B 需要比对象 A 或 "Bad Things Happen".
活得更久智能指针(shared_ptr、unique_ptr)让对象 A 强制执行对象 B 不能删除的规则,直到对象 A 完成它,所以它们是个好主意。
将对象B复制到对象A中也为对象B的生命周期提供了必要的保证,但你需要知道现在有两个独立的对象B。这是否可以接受由您决定。
指针和引用通常是有问题的,因为 A 无法控制 B 的生命周期,但有时程序的整体结构意味着这不是问题。
总结:了解您的对象生命周期并知道您可以使用哪些工具。
(甚至是原始的)指针肯定没有错。你只需要负责。如果您希望它引用的对象发生变化,请通过指针实现该成员,否则添加另一个间接级别是没有意义的,它在内存使用和 CPU 时间方面都会更有效率常规成员变量。
A Person
class 应该有它的 age
成员作为一个数字,而不是作为一个指针,但是一个人的 residence
可能是一个指针,到引用住宅集合中的对象。而且并不总是需要人管理住所或使用智能指针,住所数据层可能完全独立于人层,将住所与人一起删除意义不大,而应该刚空出来。
这完全取决于您需要什么,对象是否需要是某物、拥有某物或仅引用外部某物。
请记住,间接访问是有代价的。这就是为什么将 age
成员实现为指针毫无意义的原因,人的寿命不会很长,你可以用一个字节来存储年龄,如果你有,你将一无所获一个年龄的集合,你为每个人引用一个,因为指针通常是 4 或 8 个字节,加上它引用的字节,你浪费内存和 CPU 时间来检索该内存地址中的数据。但是住宅可能是一个大对象,而且它与人的耦合并不紧密,因此将一个人的住宅实现为一个指针是有意义的。虽然智能指针会在删除人员时删除住宅,但常规指针只允许您在人员的析构函数中从住宅的居民引用中注销该人员。
还要考虑这个——你可能在对象之间有抽象,例如你可能真的不想让每个人都有一个住所指针,或者在多个住所的情况下有多个,事实上一个人可能对住所的存在,您仍然可以通过使用第三方对象建立人与住所之间的关系而无需保留成员,例如对象和住所指针的地图,因此您可以获得一个人的住所,如果any 通过查询地图,它比为每个人都有一个住所指针要慢,但它会节省内存,而不是为每个人实例存储住所指针。可能根本没有住所。
至于抽象 classes - 因为它们不能被实例化,所以你不能将它们作为成员对象。因为这个想法是定义一个接口,你很可能是一个指针并利用虚拟分派和多态性。请注意,除了聚合之外,您还可以使用继承。如果每个对象只有一个策略,并且每个对象都有它,那么聚合和继承都可以解决问题。在某些对象有多个而其他对象有 none 的情况下,您可以采用上一段中提到的解耦设计。另请注意,使用继承 Strategy::doStuff()
将成为该对象的 vtable 的一部分,这意味着该特定类型的每个对象最终将执行相同的代码,而使用聚合或其他耦合时,该策略可以设置为每个实例库。