是否可以将对非指针成员变量的引用作为指针返回?

时间:2012-09-07 10:19:42

标签: c++ pointers memory-management reference smart-pointers

我最近遇到了一些看起来像这样的C ++代码:

class SomeObject
{
private:
    // NOT a pointer
    BigObject foobar;

public:
    BigObject * getFoobar() const
    {
        return &foobar;
    }
};

我问程序员为什么他不只是把foobar作为指针,他说这样他就不用担心分配/释放内存了。我问他是否考虑使用一些智能指针,他说这也很有用。

这是不好的做法吗?这似乎非常hackish。

6 个答案:

答案 0 :(得分:6)

这是完全合理的,而不是任何“hackish”;虽然返回引用以指示对象肯定存在可能被认为更好。指针可能为null,并可能导致一些人认为他们应该在使用后将其删除。

对象必须存在于某个地方,并且作为对象的成员存在通常与其他任何地方存在一样好。通过从拥有它的对象中单独动态分配来增加额外的间接级别会降低代码的效率,并增加确保正确解除分配的负担。

当然,如果成员函数返回非const引用或指向成员的指针,则它不能是const。这是使其成为成员的另一个好处:const上的SomeObject限定符也适用于其成员,但不适用于它只有指针的任何对象。

唯一的危险是当某人仍然有指针或引用它时,该对象可能会被销毁;但是,无论你如何管理,这种危险仍然存在。如果对象生命周期太复杂而无法管理,那么智能指针在这里可以提供帮助。

答案 1 :(得分:3)

您正在返回指向成员变量而非引用的指针。这是糟糕的设计。 您的类管理 foobar 对象的生命周期,并通过返回指向其成员的指针,使您的类的使用者能够继续使用超出 SomeObject 对象生命周期的指针。此外,它还允许用户根据需要更改 SomeObject 对象的状态。

相反,你应该重构你的类,以包含将在SomeObject类中作为方法在foobar上完成的操作。

PS。考虑正确命名您的类。当你定义它是一个类。实例化时,你有一个该类的对象。

答案 2 :(得分:2)

通常认为返回指向内部数据的指针并不理想;它可以防止类管理对自己数据的访问。但是如果你想这样做,我认为这里没有什么大问题;它简化了内存管理。

答案 3 :(得分:1)

只要调用者知道getFoobar()对象析出时从SomeObject返回的指针变为无效,就可以了。这些附带条件和警告在较旧的C ++程序和框架中很常见。

即使是现有的图书馆也必须出于历史原因这样做。例如std::string::c_str,它返回一个指向字符串中内部缓冲区的指针,当字符串析构时它变为不可用。

当然,在大型或复杂的计划中很难确保这一点。在现代C ++中,首选方法是尽可能简化“值语义”,以便每个对象的生命周期由以微不足道的方式使用它的代码控制。所以没有裸指针,没有明确的newdelete调用分散在你的代码周围等等,所以不需要程序员手动确保他们遵守规则。

(如果您完全无法避免对象生命周期的共同责任,那么您可以使用智能指针。)

答案 4 :(得分:1)

  

这是不好的做法吗?这似乎非常hackish。

是的。如果类在指针执行之前超出范围,则成员变量将不再存在,但仍然存在指向它的指针。任何取消引用类指针销毁的指针都会导致未定义的行为 - 这可能会导致崩溃,或者可能导致很难找到任意内存被读取并被视为BigObject的错误。

  

如果他考虑使用一些智能指针

使用智能指针,特别是std::shared_ptr<T>或boost版本,在技术上可以在这里工作并避免潜在的崩溃(如果你通过共享指针构造函数分配) - 但是,它也会混淆谁拥有该指针 - 该类,还是来电者?此外,我不确定您只能将对象的指针添加到智能指针。

这两点都涉及到从类中获取指针的技术问题,但真正的问题应该是“为什么?”就像在“你为什么要从一个班级返回一个指针?”在某些情况下,这是唯一的方法,但通常情况下,您不需要返回指针。例如,假设需要将变量传递给C API,该API接受指向该类型的指针。在这种情况下,您可能会更好地在类中封装C调用。

答案 5 :(得分:0)

这里有两个不相关的问题:

1)您希望SomeObject的实例如何管理所需的BigObject实例?如果SomeObject的每个实例都需要自己的BigObject,则BigObject数据成员是完全合理的。在某些情况下,您希望做出不同的事情,但除非出现这种情况,否则坚持使用简单的解决方案。

2)您是否希望SomeObject用户可以直接访问其BigObject?默认情况下,这里的答案是“#34; no&#34;”,基于良好的封装。但如果你愿意,那就不会改变(1)的评估。此外,如果您愿意,您不一定需要通过指针来实现 - 它可以通过引用甚至是公共数据成员。

可能出现第三个可能改变(1)的评估的问题:

3)您是否希望SomeObject用户BigObject直接访问他们在SomeObject实例的生命周期内继续使用的shared_ptr实例?如果是这样,那么数据成员当然不好。正确的解决方案可能是SomeObject::getFooBar,或者BigObject可能是每次调用时返回不同getFooBar()的工厂。

总结:

  • 除了它没有编译的事实(const BigObject*需要返回const &)之外,到目前为止,没有理由认为这段代码是错误的。其他问题可能会导致错误。
  • 返回const *而不是foobar可能是更好的风格。您返回的内容与BigObject是否应为foobar数据成员无关。
  • 当然没有&#34;只是&#34;关于使BigObject指针或智能指针 - 任何一个都需要额外的代码来创建一个{{1}}的实例来指向。