防止早期物体破坏

时间:2016-09-08 20:35:28

标签: c++ variables destructor object-lifetime

在C ++中,如果我写

token make_token() { return token{}; }

然后按如下方式使用

void use_token()
{
  make_token();
  // extra code
}

没有为变量分配令牌,token的析构函数会在执行额外代码之前触发。如何让析构函数只在函数结束时触发而不必创建变量?

注意:我想完全避免制作变量。我知道我可以做auto& t = make_token()或类似的事情,但我想通过返回 (我不知道是什么)来解决这个问题并没有解决这个问题立即

为什么我想要这个:基本上,在我的应用程序(编程语言的编译器)中,我将这些东西称为令牌。令牌的构造函数可以放置{和缩进,然后它的析构函数可以放置}并取消缩进。我认为设置按值返回这些标记的函数是个好主意,但我实际上并不想将它们分配给任何值,因为标记是无用的并且没有函数。

为了减轻混淆,我的token 是一个词法令牌。我使用工作token代替工作cookie。它意味着在构造函数中执行某些操作,等到其作用域结束,然后在其析构函数中执行某些操作。就是这样。顺便说一下,如果我用C#写这个,我会写类似

的东西
 using (make_token())
 {
   // my code here
 }

它会按预期工作。但事实证明,在C ++中很难做到这么简单。

6 个答案:

答案 0 :(得分:8)

是。您可以使用常量引用。这在C ++中被称为最重要的const ,它是一个并不广为人知的特性。

以下是您的工作方式:

void use_token()
{
  const token& myToken = make_token();
  // now myToken is alive until the end of this function.
}

但是你必须严格按值返回才能工作(你在你提供的代码中这样做)。

不相信这一点的人,请在攻击帖子之前亲自尝试。

答案 1 :(得分:8)

像这样:

make_token(),
[](){ /* extra stuff */ }();

确保事后洗手:)

答案 2 :(得分:4)

如果您能够使用C ++ 11或更高版本,则可以编写类似以下内容的模板函数:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">

  <div class="card moveUp" ng-repeat="x in [1,2,3,4,5,6] track by $index" ng-style="{'animation': 'moveUp ' + ($index / 2) + 's ease forwards'}">

    <span class="fec">Updated Aug 29, 2016</span>

    <span class="title">Internet Banner Advertising…</span>

  </div>

</div>

在澄清为什么需要之后编辑:

对于创建令牌以放入{}和缩进的特定用例,您可以创建一个专门命名的包装函数,以明确发生了什么:

template <typename T, typename Functor>
void keep_alive(T&&, Functor f) {
    f();
}

...
void use_token() {
    keep_alive(make_token(), [&] {
        // rest of body of function
    });
}

答案 3 :(得分:2)

我们在这里有经典的XY problem

所以对于C#代码:

using (make_token())
{
  // my code here
}

创建一个类令牌:

class token {
public:
    token() { // calling make_token(); }
    ~token() { // destroying token }
};

然后使用它:

{
    token tok;
    // some stuff here
    {
        token tok;
        // some other stuff here
    }
}

所以

  1. 对于C ++开发人员来说,这种用法很明显,而且您的C ++ API也很容易使用。
  2. 关于创建唯一变量名称的问题的论点是错误的,如图所示,您可以使用相同的名称。
  3. 您不必告诉任何人不要使用此变量,因为它只有构造函数和析构函数。
  4. 如果有必要,你可以把它放到宏中,但是我会发现它更难以使用。

答案 4 :(得分:1)

好吧,你可能认为你可以收到该函数返回的值;

void use_token()
{
  auto nonsense = make_token();
  // extra code
}

即便如此,你知道吗(Pre-C ++ 17)......当RVO没有发生时,还有两次析构函数调用吗?

const引用The Quantum Physicist's answer表示,这是最好的解决方法。

答案 5 :(得分:0)

我感到你的痛苦,auto = make_token()会很有用。然而...

您可能遇到XY问题。你不应该这样做:

with_token([&]{ ... });

即,令牌生成器/构造函数采用lambda?如果您不想在lambda中编写return 以从创建令牌的实际函数返回,那么这应该

另一种方法是,如果你只是想让一个人“知道这个名字”,那就是臭名昭着的模式:

template<typename T>
struct Keeper
{
    const T t;
    char b;
    Keeper(const T& t_)
        : t(t_) {}
    char* begin() { return &b; }
    char* end()   { return begin() + 1; }
};

template<typename T>
auto make_keeper(const T& t)
{
    return Keeper<T>(t);
}

void f()
{
    for (char c : make_keeper(make_token()))
    {
        // now try to name t or Keeper<T>(t) here
    }
}

如果愿意,您可以添加移动原理图和完美的转发。