运算符方法并返回“超出范围”的对象?

时间:2018-03-31 16:06:37

标签: c++ c++11 memory-management copy-constructor

#include <iostream>
using namespace std;

class Box {
public:
  double getVolume(void) {
     return length * breadth * height;
  }
  void setLength( double len ) {
     length = len;
  }
  void setBreadth( double bre ) {
     breadth = bre;
  }
  void setHeight( double hei ) {
     height = hei;
  }

  // Overload + operator to add two Box objects.
  Box operator+(const Box& b) {
     Box box; //local object?
     box.length = this->length + b.length;
     box.breadth = this->breadth + b.breadth;
     box.height = this->height + b.height;
     return box;
  }

private:
  double length;      // Length of a box
  double breadth;     // Breadth of a box
  double height;      // Height of a box
};

代码来源:https://www.tutorialspoint.com/cplusplus/cpp_overloading.htm。以上运营商如何运作?令我感到困惑的是,与Java相反,在C ++ Box框中创建了一个对象,但该方法返回的对象的生命周期仅限于方法范围(运算符)。

所以我尝试了另一个例子:

template <typename T>
class SmartPointer
{
    T *ptr;
    int numElem; //-1 if an element. >=0 if an array
public:
    SmartPointer(T pNum);
    SmartPointer();
    SmartPointer(T *pArray, int pSize);
    ~SmartPointer();
    SmartPointer(std::initializer_list<T> nums);
    T getValue() const;
    T getValue(int index) const;
    void setValue(T pNum);
    void setValue(T pNum, int index);
    int getNumElem() const;
    SmartPointer<T> operator+ (const SmartPointer<T>& ptr);
    SmartPointer<T> operator- (const SmartPointer<T>& ptr);
    SmartPointer<T> operator* (const SmartPointer<T>& ptr);
};

template <class T>
SmartPointer<T> SmartPointer<T>::operator+ (const SmartPointer<T>& p_ptr)
{
        int pSize = this->getNumElem();
        T tempArray[pSize] = {0};
        for(int i = 0; i < this->getNumElem(); i++)
        {
            int result = this->getValue(i) + p_ptr.getValue(i);
            tempArray[i] = result;
        }
        SmartPointer<T> result(tempArray, pSize); (line 60)
        return result; (line 61)
    }
}

我正在尝试实现smartpointer,我想重载+就像它是一个分量加法(如向量加法)。

然后,如果我运行以下代码:

SmartPointer<int> sPointer6({10,11,12});
SmartPointer<int> sPointer7({10,11,12});
SmartPointer<int> sPointer8 = sPointer6 + sPointer7;
cout << sPointer8.getValue(0) << endl; //getValue(index)
cout << sPointer8.getValue(1) << endl;
cout << sPointer8.getValue(2) << endl;

我得到以下输出:

1310912
1338712
24

但如果我用

替换第60行和第61行
return SmartPointer<T>(tempArray, pSize);

然后我得到以下输出:

20
22
24

为什么我会得到不同的输出?为什么第一个例子工作但不是smartpointer示例?

2 个答案:

答案 0 :(得分:1)

您的模板类Chriss-MacBook-Pro-2:build louisduplessis$ cmake .. && make In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/utility:203: /Library/Developer/CommandLineTools/usr/include/c++/v1/cstdint:158:8: error: no member named 'uint8_t' in the global namespace using::uint8_t; ~~^ /Library/Developer/CommandLineTools/usr/include/c++/v1/cstdint:159:8: error: no member named 'uint16_t' in the global namespace using::uint16_t; ~~^ /Library/Developer/CommandLineTools/usr/include/c++/v1/cstdint:160:8: error: no member named 'uint32_t' in the global namespace using::uint32_t; ~~^ /Library/Developer/CommandLineTools/usr/include/c++/v1/cstdint:161:8: error: no member named 'uint64_t' in the global namespace using::uint64_t; ~~^ /Library/Developer/CommandLineTools/usr/include/c++/v1/cstdint:178:8: error: no member named 'uint_fast8_t' in the global namespace using::uint_fast8_t; ~~^ /Library/Developer/CommandLineTools/usr/include/c++/v1/cstdint:179:8: error: no member named 'uint_fast16_t' in the global namespace using::uint_fast16_t; ~~^ /Library/Developer/CommandLineTools/usr/include/c++/v1/cstdint:180:8: error: no member named 'uint_fast32_t' in the global namespace using::uint_fast32_t; ~~^ fatal error: too many errors emitted, stopping now [-ferror-limit=] 20 errors generated. make[2]: *** [CMakeFiles/path_planning.dir/src/main.cpp.o] Error 1 make[1]: *** [CMakeFiles/path_planning.dir/all] Error 2 make: *** [all] Error 2 未定义SmartPointer和复制构造函数,因此定义了默认的operator=或复制构造函数。返回的副本operator=引用已释放的数组sPointer8。它是UB,它是由于违反了自由规则。

如果您使用ptr代替C-array std::vector<T>及其大小ptr,则不会遇到此类错误。

答案 1 :(得分:1)

  

我很困惑的是,与Java相反,在C ++ Box框中会在堆栈上创建一个对象,但该方法返回的对象的生命周期仅限于方法范围(运算符)。

  Box operator+(const Box& b) {
     Box box; //local object?
     // ...
     return box;
  }

正确,但这不是问题,因为对象是复制Box对象定义了一个隐式的默认“复制构造函数”(带有签名Box (Box const & b))和一个隐式operator=()(带有签名Box & (Box const & b)。从C ++ 11开始,也是一个“移动构造函数”(带签名Box (Box && b))和Box & operator=(Box const & b)

所以,当你写

Box a, b, c;

// ...

Box d { a + b };
c = a + b;

operator+()创建在Box中复制(或移动)的临时resultd),通过复制或移动构造函数,或c在它被破坏之前,通过operator=()

对于Box,默认的复制/移动构造函数和operator=()是正常的,因为没有涉及内存分配或其他复杂操作(它们只是复制length,{{1 },breadth)。

问题出现在您的height(如果我理解正确的话)动态分配的内存(SmartPointer)。

默认构造函数(等等)已经不行了,因为它复制立即解除分配的ptr(破坏临时对象)的值(如果已添加{{ 1}} ptr

结果就是当你写

delete[]

~SmartPointer()中的SmartPointer<int> sPointer8 = sPointer6 + sPointer7; 指向一个空闲的内存区域。并且程序可以用于其他目的(其他变量)。

ptr

这是因为(我想)在临时sPointer8中为So, when you get 1310912 1338712 24 result[0]保留的内存是免费的,并且可以重用一个或多个其他变量。

你得到的结果不同

result[1]

sPointer6 + sPointer7

这是一个纯粹的死亡,因为在这两种情况下你都在访问免费内存,在这两种情况下都是UB(Undefined Bahaviour)。

UB的意思是:任何事情都可能发生。

解决方案:编写复制/移动构造函数,以及 SmartPointer<T> result(tempArray, pSize); return result; 来管理 return SmartPointer<T>(tempArray, pSize); 中分配的内存的复制和复制。

或者,更好的是,避免直接管理内存并使用标准库中提供的容器/智能指针。

另一点:

operator=()

不是(标准C ++):您无法使用运行时值初始化C样式数组。