【问题标题】:C++ operator overloading return pointerC++ 运算符重载返回指针
【发布时间】:2015-08-12 18:24:53
【问题描述】:

我是 C++ 编程的新手,我想知道一些事情:

每当我在 C++ 中看到运算符重载时,都是这样完成的:

#ifndef STONE_H
#define STONE_H

class Stone {
    private:
    int weight;

    public:
    .......

    Stone operator+(const Stone& s) {
        Stone stone;
        stone.weight = this->weight + s.weight;
        return stone;
    }

    .......
}


#endif

但是当调用“+”操作符时,它会创建一个对象“stone”,并返回它的一个副本。处理大对象时,这对性能没有好处吗?

使用动态内存不是更好吗,如下例所示:

Stone * operator+(const Stone& s) {
    Stone * stone = new Stone;
    stone->weight = this->weight + s.weight;
    return stone;
}

还是我看错了?

提前致谢

【问题讨论】:

  • 谁会释放这些对象?在这里a+b+c?
  • 始终将 new 与 delete 配对(使用二元运算符可能会导致内存泄漏)。第一种方法很好(看看返回值优化)
  • 您缺少的是所有严肃的编译器都实现了 NRVO,这意味着他们通常可以避免在这种情况下复制返回的对象。相反,它们在适当的位置创建返回值。搜索 RVO 和 NRVO 以获取有关其工作原理以及如何应用于此类运算符重载的更多信息

标签: c++ operator-overloading dynamic-memory-allocation


【解决方案1】:

尝试推理并不是评估性能特别准确的方法:您需要实际编写一个程序来衡量一种实现是否比另一种更好。

有多种方式根本不会发生复制;命名返回值优化(NRVO)和移动赋值是这里的相关思想。

即使您决定要执行您的建议,您也不应该按照您的方式实现它,因为它的语义错误:您已经让 operator+ 返回一个指向事物的指针,而不是一个东西。此外,使用指针(尤其是裸指针)是有风险的,因为它会给你更多犯错的机会。

如果你想按照这些思路实现一些东西,你需要将指针语义包装在一个提供值语义的类中。

【讨论】:

    【解决方案2】:

    原来按照现在的标准,这有点不同:

    #include <iostream>
    
    class big {
      int* v; // Demonstration purposes. A smart pointer or a standard container would be better.
      public:
      big& operator+=(big& o) {
        for (int i=0;i<1000;++i) {
          v[i] += o.v[i];
        }
        return *this;
      }
    
      big(int val = 0)
          :    v{new int[1000]} // We're using RAII to prevent resource leaking.
      {
        std::cout << "a construction.\n";
        for (int i=0;i<1000;++i) {
          v[i] = val;
        }
      }
    
      // Copy constructor
      big(big& o) 
          :    v{new int[1000]}
      {
        std::cout << "a copy construction.\n";
        for (int i=0;i<1000;++i) {
          v[i] = o.v[i];
        }
      }
    
      // Move assignment
      big& operator=(big&& o) {
        std::cout << "a move assignment.\n";
        if (v) delete[] v;
        v = o.v;
        o.v = nullptr;
      }
    
      // Move constructor
      big (big&& o) {
        std::cout << "a move construction.\n";
        v = o.v;
        o.v = nullptr;
      }
    
      ~big() {
        if (v) delete[] v;
      }
    };
    
    // a will be move-constructed if using a temporary, or copy-contructed if not.
    // The result will always be passed by a cheap move
    big operator+(big a, big& b) {
      return std::move(a += b);
    }
    
    int main() {
      big a{1};
      big b{2};
      big c{3};
    
      big d = a+b+c;
    }
    

    输出:(添加了 cmets)

    a construction. // Constructed a
    a construction. // Constructed b
    a construction. // Constructed c
    a copy construction. // a+b <- a copied into parameter "a" of operator+. b is passed by reference.
    a move construction. // The temporary from the operation above, moved into parameter "a" of operator+. c is passed by reference.
    a move construction. // d is move constructed from the temporary generated by a+b+c.
    

    【讨论】:

      猜你喜欢
      • 2012-09-08
      • 2021-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-14
      • 2014-12-24
      • 1970-01-01
      相关资源
      最近更新 更多