在C++中返回对象的最佳方法是什么?

13

我在C++方面很菜,那么返回对象的更好方法是什么?我来自脚本世界,在那里对象总是引用,并且正试图实现相同的概念...我基于当在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?


9
我来自脚本编程的世界,那里对象总是引用,我试图实现同样的概念。你为什么要在C++中把所有东西都传递为引用?这在C++中并不自然。你提到的“经验法则”可能更好地表述为“尽可能使用值(和副本),否则使用引用,只有作为最后一招才使用指针”。 - James McNellis
考虑到您的背景,我强烈建议您学习有关智能指针的知识,以便为堆分配的对象获得自动内存管理。 - Emile Cormier
6个回答

11
您需要调整Channel类本身,使其无法被复制。如果它是可复制的,用户可以复制它,而您所做的任何事情都无法阻止它。
如果复制不是有意义的操作,则可以“禁用”它。只需将复制构造函数(Channel(const Channel&))和赋值运算符(Channel& operator=(const Channel&))定义为私有。然后,任何试图复制该类的尝试都会导致编译错误。
顺便说一句,正如其他人所提到的,C++不是您熟悉的脚本语言。不是所有东西都是引用,通过假装否认这一点,您只会让自己陷入困境。在C++中,通常会在堆栈上分配对象,并传递按值而不是传递引用和指针。

4

返回引用(或const 引用)是getter方法向调用者直接提供成员变量访问权限的正常方式,因此我建议使用第二个版本的getChannel()

如果您想防止调用者制作不适当的Channel副本,可以通过私有化其复制构造函数来实现这一点。(如果您想防止所有东西都创建副本,甚至包括Channel自己,可以将其构造函数声明为private,然后不实现它。)但是,只有在制作副本真的没有意义的情况下,例如,如果该类表示无法复制的某种基础资源时,才应该这样做。不要仅仅因为认为调用者不应该需要复制就禁止复制;这是调用者自己的决定。


2

如果复制不会对您的目的产生昂贵的开销,并且您不需要能够更改原始对象,则返回对象本身的副本。这应该是默认设置。

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
 }

1
你即使使用指针返回版本,也无法阻止调用者创建一个新实例。
Channel* channel = new Channel(*layer.getChannel());

我知道有一种方法可以实现这个目标。(例如,将Channle的构造函数设置为私有,这样只有它的静态成员函数或友元函数才能创建它。)然而,我认为这不是你问题的重点。

重点是当你让成员函数返回引用或指针时,你给调用者提供了选择,他可以选择是否要复制它或引用它。此外,你可以通过添加const使你的意图更加清晰,使其只读。

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


1

既然你返回了一个对象的引用,那么你就直接给了类的使用者访问该对象的权限。如果是这样,为什么要将该对象定义为私有的呢?还不如把它定义为公有的。


我正在尝试防止任何其他对象设置Layer.channel。通过返回一个引用,我假设仍然可以将引用设置为新通道,对吗?我的头很疼。 - ansiart

-1

最重要的是,在编写代码时保持可读性。 “入乡随俗”很重要。你只需写一次,但需要维护你的代码的每个人都必须阅读它。如果突然间你的代码遵循不同的准则,那么他们需要首先弄清楚你的风格,然后再理解你在做什么...

我见过的一个非常有效的方法是使用指针来更改事物,使用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.

其他建议将复制操作变成编译错误的答案也是不错的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接