有没有办法让 C++ 编译器检测并警告/错误此对象生命周期错误?

问题描述 投票:0回答:1

考虑下面的玩具示例代码。 它有一个太大而无法复制的数据结构

MyDataStructure
和一个小迭代器类
MyDataIterator
,调用者可以实例化它来迭代结构中的数据。

在代码的玩具版本中,数据结构仅包含 10 个整数 (1000-1009),并且

WORKING CODE
中的
main()
部分按照预期正确地迭代它们。

但是,

BROKEN CODE
中的第二个 (
main()
) 部分无法正确迭代它们;它调用未定义的行为,因为它调用
GetDataStructureByValue()
而不是
GetDataStructureByReference()
,这意味着
MyDataStructure
存储在其
iter
私有成员变量中的
_ds
引用在第一次循环迭代之前成为悬空引用。

这是一个有点阴险的问题,因为在非玩具代码中,并不总是很容易从函数的名称中判断/记住它是按值返回还是按引用返回,但前一种情况会导致非-明显的运行时错误,而后一种情况工作正常。

我的问题是,是否有任何合理的方法可以让现代 C++ 编译器出错或至少警告有问题的情况? 我怀疑没有,但我还是要问,因为如果可以以某种方式自动为我标记此问题的实例,我会感到更安全。

玩具程序的源代码如下,后面是其在我的 Mac 上运行 XCode 的输出示例:

#include <stdio.h>

class MyDataStructure
{
public:
   MyDataStructure()
   {
      printf("MyDataStructure CTOR %p\n", this);
      for (int i=0; i<10; i++) _data[i] = i+1000;
   }

   ~MyDataStructure()
   {
      printf("MyDataStructure DTOR %p\n", this);
      for (int i=0; i<10; i++) _data[i] = -1;  // just to make the symptoms more obvious
   }

   bool IsPositionValid(int pos) const {return ((pos >= 0)&&(pos < 10));}

   int GetValueAt(int pos) const {return _data[pos];}

private:
   int _data[10];
};

const MyDataStructure & GetDataStructureByReference()
{
   static MyDataStructure _ds;
   return _ds;
}

MyDataStructure GetDataStructureByValue()
{
   MyDataStructure _ds;
   return _ds;
}

class MyDataIterator
{
public:
   MyDataIterator(const MyDataStructure & ds) : _ds(ds), _idx(0) {/* empty */}

   bool HasValue() const {return _ds.IsPositionValid(_idx);}
   int  GetValue() const {return _ds.GetValueAt(_idx);}

   void operator++(int) {_idx++;}

   const MyDataStructure & GetDataStructure() const {return _ds;}

private:
   const MyDataStructure & _ds;
   int _idx;
};

int main(int, char **)
{
   {
      // The following line of code is okay; it counts from 1000 to 10009, as expected
      printf("WORKING CODE:\n");
      for (MyDataIterator iter(GetDataStructureByReference()); iter.HasValue(); iter++) printf("iter returned %i from %p\n", iter.GetValue(), &iter.GetDataStructure());
   }

   {
      // The following line of code is buggy because, the MyDataStructure object that (iter) keeps a reference to
      // gets destroyed before the first iteration of the loop.
      printf("\nBROKEN CODE:\n");
      for (MyDataIterator iter(GetDataStructureByValue()); iter.HasValue(); iter++) printf("iter returned %i from %p\n", iter.GetValue(), &iter.GetDataStructure());
   }

   printf("\nEND OF PROGRAM\n");
   return 0;
}

...示例输出:

$ ./a.out
WORKING CODE:
MyDataStructure CTOR 0x1015c4000
iter returned 1000 from 0x1015c4000
iter returned 1001 from 0x1015c4000
iter returned 1002 from 0x1015c4000
iter returned 1003 from 0x1015c4000
iter returned 1004 from 0x1015c4000
iter returned 1005 from 0x1015c4000
iter returned 1006 from 0x1015c4000
iter returned 1007 from 0x1015c4000
iter returned 1008 from 0x1015c4000
iter returned 1009 from 0x1015c4000

BROKEN CODE:
MyDataStructure CTOR 0x7ff7be93d198
MyDataStructure DTOR 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198
iter returned -1 from 0x7ff7be93d198

END OF PROGRAM
MyDataStructure DTOR 0x1015c4000
c++ iterator lifetime
1个回答
0
投票

一个简单的事情就是阻止使用临时对象创建迭代器。 如果以

的形式向
MyDataIterator

添加另一个构造函数
MyDataIterator(MyDataStructure &&) = delete;

那么这个构造函数将是重载决策选择的构造函数,因为它更适合右值。 由于它被删除,编译器将发出错误,因为您无法调用已删除的构造函数。

© www.soinside.com 2019 - 2024. All rights reserved.