【问题标题】:Best way to return an object in c++?在 C++ 中返回对象的最佳方法?
【发布时间】:2011-04-24 00:30:21
【问题描述】:

我对 c++ 很陌生,返回对象的更好方法是什么?我来自对象始终是引用的脚本世界,并且正在尝试实现相同的概念......我基于When to pass by reference and when to pass by pointer in C++?,其中一位用户说:“一个好的经验法则:”使用可以的时候参考,必要的时候指点”。”

// basic layer class
class Layer { private: Channel channel; // NEVER NULL };

// return object by pointer
Channel *Layer::getChannel() {
    return &channel;
};

// return by reference
Channel& Layer::getChannel() {
    return channel;
};

第二个版本的问题是编译器会接受这一行:

Channel channel = layer.getChannel();  // creates a copy BAD

什么时候应该:

Channel &channel = layer.getChannel();  // reference good

有没有办法强制第二个选项的调用者强制它不创建新频道,或者第一个选项更好,即使它永远不会为 NULL?

【问题讨论】:

  • “我来自脚本世界,其中对象始终是引用,并且我正在尝试实现相同的概念”为什么要尝试在 C++ 中将所有内容作为引用传递?在 C++ 中这样做是不自然的。您引用的“经验法则”可能会更好地表述为“尽可能使用值(和副本),否则使用引用,并且仅作为最后的手段使用指针。”
  • 考虑到您的背景,我强烈建议您了解智能指针,以便获得堆分配对象的自动内存管理。

标签: c++


【解决方案1】:

您需要调整Channel 类本身,使其不可复制。如果它是可复制的,那么用户可以复制它,你做什么都不能阻止它。

如果复制不是有意义的操作,那么您可以“禁用”它。只需将复制构造函数 (Channel(const Channel&)) 和赋值运算符 (Channel& operator=(const Channel&)) 定义为私有。那么任何复制类的尝试都会导致编译错误。

另外,正如其他人所提到的,C++ 不是您熟悉的脚本语言。一切都不是参考,你只是假装自己进入一个痛苦的世界。在 C++ 中,通常在堆栈上分配对象,并按值传递对象,而不是传递引用和指针。

【讨论】:

    【解决方案2】:

    返回一个引用(或 const 引用)是 getter 方法让调用者直接访问成员变量的正常方式,所以我推荐getChannel() 的第二个版本。

    如果您想防止调用者制作Channel 的不适当副本,您可以通过将其复制构造函数设为私有来实现。 (如果您想阻止 everything 复制,甚至是 Channel 本身,您可以将构造函数声明为私有,然后不实现它。)但只有在复制实际上是荒谬的,例如如果该类表示某种无法复制的基础资源。不要仅仅因为您认为调用者不应该需要而禁止复制;这是调用者的决定。

    【讨论】:

      【解决方案3】:

      当复制对您的目的而言成本不高且您不需要更改原始对象时,返回对象本身的副本。这应该是默认值。

      Channel Layer::getChannel() {     return channel; };
      

      当复制成本很高或您可能想要更改值时,通过引用或指针返回。通过引用返回允许您执行以下操作:

      layer.getChannel().clear();
      

      让它作用于该层中的通道。

      返回一个指针类似于返回一个引用,只是它给了你更多的灵活性,因为指针根本不能指向任何对象。当我希望能够使用将“通道”存储在另一个类中时,我经常使用指针。然后我会做

       class MyClass
       {
           // ...
           void setChannel(Channel *pC) { m_pChannel = pC; }
       private:
           Channel * m_pChannel;  // pointer to a channel that came from layer
       }
      

      【讨论】:

        【解决方案4】:

        由于您要返回对对象的引用,因此您正在为该类的用户提供对该对象的直接访问权限,如果您要这样做,为什么要将对象设为私有?只需公开即可。

        【讨论】:

        • 我试图阻止任何其他对象设置 Layer.channel。通过返回参考,我假设仍然可以将参考设置为新频道正确吗?我的头好痛。
        【解决方案5】:

        即使使用指针返回版本,也无法阻止调用者创建新实例。

        Channel* channel = new Channel(*layer.getChannel());
        

        我知道有办法实现这个目标。 (例如,将 Channle 的 ctor 设为私有,因此只有它的静态成员函数或其友元函数才能创建它。)但是,我认为这不是您问题的重点。

        关键是当你让成员函数返回引用或指针时,你给调用者选项,他可以选择他是想复制它还是引用它。此外,您可以通过添加const 使其成为只读来使您的意图更加明确。

        对于您的情况,我会选择参考返回版本,因为 Channel 不能为空。如果您不希望他们更改成员变量,请返回 const 引用。请记住,没有单一的最佳方法来决定返回值类型,因为它取决于您想说什么。希望能帮助到你! :)

        【讨论】:

          【解决方案6】:

          最重要的是保持周围代码的可读性。 “在罗马做到入乡随俗。”很重要。您编写一次,但必须维护您的代码的每个人都必须阅读它。如果突然之间你的代码遵循了与你周围的每个人不同的准则,这意味着他们需要首先弄清楚你的风格,然后弄清楚你在做什么......

          我见过的一种非常有效的方法是为您更改的内容提供指针,为您未更改的内容提供 const 引用:

          class Passenger {
            ...
          };
          
          class Car {
          public:
            int speed() const { return speed_; }
            void set_speed(int speed) { speed_ = speed; }
            const Passenger& passenger() const { return pass_;}
            Passenger* mutable_passenger() { return &pass_; }
          
          private:
            int speed_;
            Passenger pass_;
          };
          

          这个类的客户可以做:

          const Passenger& pass = car.passenger();  // no copy, but don't need to deal with NULL ptrs.
          

          其他建议复制编译错误的答案是好的。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-06-16
            • 1970-01-01
            • 2015-12-20
            • 2013-08-29
            • 2014-12-29
            • 1970-01-01
            相关资源
            最近更新 更多