gpt4 book ai didi

c++ - 对象已在声明时初始化?

转载 作者:行者123 更新时间:2023-12-04 01:31:23 24 4
gpt4 key购买 nike

我试图理解 C++ 中的一些东西。基本上我有这个:

class SomeClass {
public:
SomeClass();
private:
int x;
};

SomeClass::SomeClass(){
x = 10;
}

int main() {
SomeClass sc;
return 0;
}

我认为 sc 是 SomeClass 类型的未初始化变量,但是从各种教程中我发现这个声明实际上是调用 SomeClass() 构造函数的初始化,而我不需要调用“sc = new SomeClass();”或类似的东西。

因为我来自 C# 世界(并且知道一点 C,但不知道 C++),所以我试图了解什么时候需要 new 之类的东西以及什么时候发布这样的对象。我发现了一个名为 RAll 的模式,它似乎无关。

这种类型的初始化称为什么,我如何知道某些东西是纯粹的声明还是完整的初始化?

最佳答案

我认为这里有几件事:

  • 自动变量与动态分配变量的区别
  • 对象的生命周期
  • RAII
  • C# 并行

  • 自动与动态

    自动变量是系统将管理其生命周期的变量。让我们暂时抛开全局变量,它很复杂,专注于通常的情况:
    int main(int argc, char* argv[])  // 1
    { // 2
    SomeClass sc; // 3
    sc.foo(); // 4
    return 0; // 5
    } // 6

    这里 sc是一个自动变量。在成功执行第 (3) 行后,保证完全初始化(即保证构造函数已运行)。它的析构函数将在第 (6) 行自动调用。

    我们一般讲一个变量的作用域:从声明点到对应的右括号;并且该语言保证在退出范围时销毁,无论是 return或异常(exception)。

    在您调用通常会导致崩溃的可怕的“未定义行为”的情况下,当然不能保证。

    另一方面,C++ 也有动态变量,即你使用 new 分配的变量。 .
    int main(int argc, char* argv[])  // 1
    { // 2
    SomeClass* sc = 0; // 3
    sc = new SomeClass(); // 4
    sc->foo(); // 5
    return 0; // 6
    } // 7 (!! leak)

    这里 sc仍然是一个自动变量,但是它的类型有所不同:它现在是一个指向 SomeClass 类型变量的指针。 .

    在线(3) sc被分配一个空指针值(C++0x 中的 nullptr),因为它不指向 SomeClass 的任何实例.请注意,该语言本身不保证任何初始化,因此您需要明确分配某些内容,否则您将获得垃圾值。

    在第 (4) 行,我们构建了一个动态变量(使用 new 运算符)并将其地址分配给 sc .请注意,动态变量本身是未命名的,系统只给了我们一个指向它的指针(地址)。

    第(7)行系统自动销毁 sc ,但是它不会破坏它指向的动态变量,因此我们现在有一个动态变量,它的地址没有存储在任何地方。除非我们使用垃圾收集器(在标准 C++ 中不是这种情况),否则我们会泄漏内存,因为在进程结束之前变量的内存不会被回收......即使这样析构函数也不会运行(如果有副作用就太糟糕了)。

    对象的生命周期

    Herb Sutter 有一篇关于这个主题的非常有趣的文章。这是 the first .

    作为总结:
  • 对象在其构造函数运行完成后立即存活。这意味着,如果构造函数抛出异常,则该对象永远不会存活(将其视为意外怀孕)。
  • 对象在其析构函数被调用后立即死亡,如果析构函数抛出(这是 EVIL),则无法再次尝试,因为您无法在死对象上调用任何方法,这是未定义的行为。

  • 如果我们回到第一个例子:
    int main(int argc, char* argv[])  // 1
    { // 2
    SomeClass sc; // 3
    sc.foo(); // 4
    return 0; // 5
    } // 6
    sc从第 (4) 行到第 (5) 行包括在内。在第 (3) 行,它正在被构建(可能由于多种原因而失败),在第 (6) 行,它正在被破坏。

    RAII

    RAII 表示资源获取即初始化。管理资源是一种习惯用法,特别是要确保资源在获得后最终会被释放。

    在 C++ 中,由于我们没有垃圾收集,所以这个习惯用法主要应用于内存管理,但它也适用于任何其他类型的资源:多线程环境中的锁、文件锁、网络中的套接字/连接等......

    当用于内存管理时,它用于将动态变量的生命周期与给定的一组自动变量的生命周期耦合,确保动态变量不会超过它们(并且不会丢失)。

    以最简单的形式,它与单个自动变量耦合:
    int main(int argc, char* argv[])
    {
    std::unique_ptr<SomeClass> sc = new SomeClass();
    sc->foo();
    return 0;
    }

    它与第一个示例非常相似,只是我动态分配了一个 SomeClass 的实例。 .然后将该实例的地址传递给 sc对象,类型 std::unique_ptr<SomeClass> (这是一个 C++0x 工具,如果不可用,请使用 boost::scoped_ptr)。 unique_ptr保证指向的对象在 sc 时会被销毁被摧毁。

    在更复杂的形式中,它可能会使用(例如) std::shared_ptr 耦合到几个自动变量。 ,顾名思义,它允许共享一个对象,并保证在最后一个共享者被销毁时该对象将被销毁。请注意,这并不等同于使用垃圾收集器,并且可能存在引用循环的问题,我不会在这里深入探讨,因此请记住 std::shared_ptr不是 Elixir 。

    因为在面对异常和多线程代码的情况下,在没有 RAII 的情况下完美管理动态变量的生命周期非常复杂,建议是:
  • 尽量使用自动变量
  • 对于动态变量,永远不要调用 delete您自己并始终使用 RAII 设施

  • 我个人认为 delete 的任何发生强烈怀疑,我总是要求在代码审查中删除它:这是一种代码异味。

    C# 并行

    在 C# 中,您主要使用动态变量 * .这就是为什么:
  • 如果你只是声明一个变量而不赋值,它的值为空:本质上你只是在操作指针,因此你有一个空指针(保证初始化,谢天谢地)
  • 您使用 new要创建值,这会调用对象的构造函数并生成对象的地址;注意动态变量的语法与 C++ 的相似之处

  • 但是,与 C++ 不同,C# 是垃圾收集的,因此您不必担心内存管理。

    被垃圾收集还意味着对象的生命周期更难理解:它们是在您要求它们时构建的,但在系统方便时销毁。这可能是实现 RAII 的一个问题,例如,如果您真的希望快速释放锁,并且该语言有许多工具可以帮助您 using关键字 + IDisposable从内存接口(interface)。
    * : 很容易检查,如果在声明一个变量之后它的值是 null ,那么它将是一个动态变量。我相信对于 int该值将是 0 表示它不是,但是自从我为类(class)项目摆弄 C# 以来已经 3 年了,所以......

    关于c++ - 对象已在声明时初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3506818/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com