【问题标题】:Can I terminate the scope of a C++ variable without ending a block?我可以在不结束块的情况下终止 C++ 变量的范围吗?
【发布时间】:2011-02-25 00:38:08
【问题描述】:

我想做类似以下模式的事情:

T* const pT = findT();
// Do some work
T* const pT2 = new T( *pT );
// Mutate the object pT2 refers to
delete pT;
// At this point, I want the scope of pT to end.
// I do not want the scope of pT2 to end

我知道我可以通过结束一个块来结束作用域,但它最终是这样的:

T* pT2 = 0;
{
    T* const pT = findT();
    // Do some work
    pT2 = new T( *pT );
    // Mutate the object pT2 refers to
    delete pT;
}

这会导致 pT2 失去它的 const 限定符,因为我必须在它声明后分配给它。

我想要我的蛋糕,我也想吃它,我想要明确的常量正确的范围!

除了结束块之外,还有什么方法可以结束变量的作用域?如果没有,是否有计划扩展标准以支持这一点?

【问题讨论】:

  • 所以你想要指针 pT2 常量,而不是它所指的对象?
  • 没错,我广泛使用 const 来消除出错的可能性并减少阅读代码的认知负担。当您看到我初始化一个 const 指针时,您不必考虑它在任何地方发生变异的可能性。
  • 仅供参考,通常是 const-ness 导致 ppl 采用 const_cast 快捷方式并导致 UB
  • 你能为pT2写一个包装类,它有一个一次性分配,一旦分配就不允许任何其他分配?

标签: c++ variables scope


【解决方案1】:

您可以使用 lambda:

T* const pT = []() -> T* {
    T* pT;
    // Do whatever the hell you want with pT
    return pT;
}();

【讨论】:

  • 这与TS拒绝的“范围”解决方案非常相似。
  • @YeenFei:除了让他达到他的目的,也就是让pT2​​ const。
【解决方案2】:

除了结束块之外,还有什么方法可以结束变量的作用域?

没有。局部变量的作用域从它被声明时开始,到它被声明的块的末尾结束。

如果没有,是否有计划扩展标准以支持这一点?

这听起来是个非常糟糕的主意:这会给有关范围、名称查找和对象生命周期的规则增加不必要的复杂性。

【讨论】:

  • @DonNeufeld 那么,在对 C++ 标准的想象扩展中,从句法和语义上讲,您将如何做到这一点?
  • 嗯,我可以看到更改生命周期规则可能会使 RAII 更难理解/不太清楚。也就是说,这是一个烦人的问题。
  • @Don:这个提议的功能的实际好处是什么?我能想到的唯一用例是(1)允许用相同的名称声明一个新变量,这显然是个坏主意,或者(2)允许提前调用对象的析构函数,其中如果您已经可以通过添加“发布”功能获得类似的功能(就像在 Scope Guard 习语中所做的那样)。一个函数不应该有太多的局部变量,以至于当前的生命周期/范围规则不足以编写清晰的代码。
  • @wilhelmtell 语法上我可能会做一些可怕的事情,比如添加一个关键字。我的第一个倾向是'symbol = void;'。但是我可以看到这可能会令人困惑,因为您不想通过从返回 void 的函数意外分配来结束变量的范围。
  • @James 我给出的例子是我遇到的最常见的想要这样做的案例。我尽量避免意外引用超出其实际有效范围的指针(如您所见,它并不总是与它们的语言范围一致)
【解决方案3】:

您所说的“块”是 C++ 中“范围”的另一个名称。这在 C++ 中定义作用域的方式。

这里的另一个选项是使用std::auto_ptr,它会在分配时转移所有权。旧指针不会清理,但实际上会为零。

【讨论】:

    【解决方案4】:

    没有。但是,您可以使用以下成语来美化您的示例(好吧,这是主观的,但是嘿):

    T* p1 = 0;
    
    if (T* const p2 = findT()) {
      p1 = new T( *p2 );
      delete p2;
    }
    

    【讨论】:

    • 我实际上经常这样做,尽管有时我的代码会因此变得高度对角线,然后我必须对其进行重构。
    【解决方案5】:

    您可以使用std::optional(或boost::optional)对对象生命周期进行细粒度控制。

    这是一个例子:

    std::optional< ClassA > a(std::in_place, 1); // ClassA constructor is called
    std::optional< ClassB > b(std::in_place, 2); // ClassB constructor is called
    a.reset(); // ClassA destructor is called
    b.reset(); // ClassB destructor is called
    

    不幸的是,变量的名字还在,它的内存还在分配中。

    还有一个技巧可以销毁字符串并释放其内存(变量本身仍然存在):

    std::string my_str;
    std::string{}.swap(my_str); // likely to free the memory
    

    它是由实现定义的,因此它实际上可能不会释放内存,但它很可能会这样做。

    【讨论】:

    • 如果您关心生成的字符串容量,则不能保证交换技巧有效,尽管它比 shrink_to_fit 更有可能。
    • 它是实现定义的,但通常swap实际上是交换内存缓冲区,因此旧缓冲区与临时字符串对象一起被销毁。
    • 通常,但不一定。
    猜你喜欢
    • 2021-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    相关资源
    最近更新 更多