Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何正确实现返回ref的操作符重载,以便轻松删除它们的工件? 假设我超载C++运算符,如 ~和=,类似于 Foobar& Foobar::operator~() const { Foobar *result = new Foobar(); // compute *result based on *this return *result; } Foobar& Foobar::operator=(Foobar& arg) { // compute *this based on arg // return *this for transitivity return *this; }_C++_Operator Overloading - Fatal编程技术网

如何正确实现返回ref的操作符重载,以便轻松删除它们的工件? 假设我超载C++运算符,如 ~和=,类似于 Foobar& Foobar::operator~() const { Foobar *result = new Foobar(); // compute *result based on *this return *result; } Foobar& Foobar::operator=(Foobar& arg) { // compute *this based on arg // return *this for transitivity return *this; }

如何正确实现返回ref的操作符重载,以便轻松删除它们的工件? 假设我超载C++运算符,如 ~和=,类似于 Foobar& Foobar::operator~() const { Foobar *result = new Foobar(); // compute *result based on *this return *result; } Foobar& Foobar::operator=(Foobar& arg) { // compute *this based on arg // return *this for transitivity return *this; },c++,operator-overloading,C++,Operator Overloading,出于向后兼容性和性能原因,操作员必须返回Foobar&,而不是Foobar或指针 然后,我的类的用户将编写如下内容: Foobar obj0, obj1; obj1 = ~obj0; 现在从~返回的new已丢失,因此无法删除该new,因此是否存在内存泄漏?如果有泄漏,如何设计它以避免泄漏 现在~(新的)返回已丢失,因此无法恢复 删除那个新的,所以没有内存泄漏吗 是的,您当前的实现中几乎存在内存泄漏。或者至少有很高的机会 如果有泄漏,如何设计它以避免泄漏 为什么不把新对象放在一边,找到一个本地

出于向后兼容性和性能原因,操作员必须返回
Foobar&
,而不是
Foobar
或指针

然后,我的类的用户将编写如下内容:

Foobar obj0, obj1;

obj1 = ~obj0;
现在从
~
返回的
new
已丢失,因此无法
删除
new
,因此是否存在内存泄漏?如果有泄漏,如何设计它以避免泄漏

现在~(新的)返回已丢失,因此无法恢复 删除那个新的,所以没有内存泄漏吗

是的,您当前的实现中几乎存在内存泄漏。或者至少有很高的机会

如果有泄漏,如何设计它以避免泄漏

为什么不把新对象放在一边,找到一个本地对象,然后按值返回它呢

我的意思是:

Foobar Foobar::operator~() {
  Foobar result;
  // compute *result based on *this
  return result;
}
我不认为您有任何理由在示例中动态分配对象

如果您真的需要一个动态分配的对象(我看不出您需要它的任何原因…),那么您可能需要使用智能指针


编辑:

既然你澄清了性能可能是个问题,我想让你看看(n)rvo以及@VaughnCato/@potatosatter已经提到的移动语义(我希望你能阅读他们的答案)。我建议读一些像和这样的文章。很可能按值返回并不像(n)rvo+move语义所认为的那样是一个性能问题。我建议在之后通过优化来实现移动语义和配置文件,以确定这是否真的是一个问题,因为这是最干净的解决方案

关于智能指针。我说的是具体的,但既然你在编辑后的回答中说你想要的是一个参考,而不是指针,那也可能不是一个完美的解决方案。你可能想调查一下


如果move/(n)rvo没有给出想要的结果,那么其他的可能性将是相当不干净的,并且在我看来,像代理对象/全局容器(可能与智能指针结合使用)或其他东西一样容易出错。但我还不擅长这类东西,你可能会对@VaughnCato/@Potatoswatter的答案发表评论。因为我自己还是一个初学者,所以我想这就是我能给你的全部建议。

你说得对,这会导致内存泄漏。正常的
运算符
不是通过引用返回,而是通过值返回。运算符也应为常量,以强制您不改变操作数。使用此方法不会出现任何内存泄漏:

Foobar Foobar::operator~() const
{
  Foobar result;
  // compute *result based on *this
  return result;
  // OR
  return Foobar(<stuff to compute result>);
}
Foobar Foobar::operator~()const
{
Foobar结果;
//基于*此计算*结果
返回结果;
//或
返回Foobar();
}

是,如果
新建
与匹配的
删除不匹配,则会导致内存泄漏

你从来没有定义过“简单”。如果你想自动删除,你不能。理论上,您可以返回一个智能指针代理对象,该对象定义
操作符Foobar&
,并从其析构函数调用
delete
。也许您可以管理与需要
Foobar&
作为返回类型的代码的向后兼容性,但这将是一种糟糕的设计。对象可能会过早地破坏自身,只提供一个悬空的引用

除了一些自动的功能外,您还可以获取您所拥有的,确保保留对返回值的引用
r
,并且始终记住调用
delete&r,即使引发异常也是如此。但是,永远不要这样做,除非成功调用
操作员

基本问题是,您对类型
Foobar&
有两种不同的语义要求。要释放内存,必须将析构函数附加到引用,这是完全不可能的

有许多真正的解决方案需要研究,例如C++11移动语义,或通过值而不是
Foobar
返回的代理容器类,以避免性能问题。

“出于性能考虑,我无法按值返回。在返回时复制整个类被认为太慢。我必须返回一个引用。”

但你的代码显然就是这么做的。它通过new创建一个类型为
Foobar
的新对象,并返回对该对象的引用。事实上你有另一个物体的副本。 如果要完全避免复制,就必须使用表达式模板之类的东西

您可以通过中间对象来实现这一点

template<class Foo>
struct InvertedFoo
{
  Foo const & to_invert;
  InvertedFoo (Foo const &foo_to_invert) 
    : to_invert(foo_to_invert) { }
};

class Foobar
{
  InvertedFoo<Foobar> operator~ (void) const
  {
    return InvertedFoo<Foobar>(*this);
  }
  Foobar& operator= (InvertedFoo<Foobar> const &arg)
  {
    // compute result based on arg.to_invert
  }
};
模板
结构反相器
{
Foo const和to_invert;
逆变器(Foo const和Foo_至_倒置)
:to_invert(foo_to_invert){
};
福巴级
{
InvertedFoo算子~(void)const
{
返回InvertedFoo(*本);
}
Foobar和operator=(InvertedFoo const和arg)
{
//基于arg.to_invert的计算结果
}
};

请注意,这会将“反转”逻辑转移到采用InvertedFoo参数的赋值运算符。

一般来说,按值返回比分配新对象并返回对该对象的引用更快。返回值优化(RVO)或命名的返回值优化(NRVO)消除了副本,并且使用新对象分配对象并不像您想象的那么便宜。如果您还没有这样做,您应该在启用优化的情况下从两个方面对其进行分析,并确保您正在尝试的工作有好处

如果确实必须返回引用,一种可能是使用全局容器:

std::list<Foobar *> global_foobars;

Foobar& Foobar::operator~() const {
  Foobar *result = new Foobar();
  global_foobars.push_back(result);
  // compute *result based on *this
  return *result;
}

我假设这个类太大了,不能随意复制。您的
Foobar
应该保留一个指向а
FoobarImpl
的智能指针。按值返回它。这是保持头脑清醒的唯一方法。谢谢。请参阅我编辑的文章。请注意,性能不受影响
std::list<Foobar> global_foobars;

Foobar& Foobar::operator~() const {
  global_foobars.emplace_back();
  return global_foobars.back();
}