【问题标题】:How are objects created in C++?C++中的对象是如何创建的?
【发布时间】:2012-07-29 17:42:25
【问题描述】:

我了解 Java,但对 C++ 了解不多。我正在尝试为 https://developers.google.com/v8/get_started 的代码主函数中的前 3 个语句编写一个类。

首先我对如何在 C++ 中创建对象有疑问。请参阅下面的代码。

HandleScope handle_scope;
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);

我认为在 C++ 中,当您声明一个类的变量时,该类的实例是在该点创建的。您不需要像在 Java 中那样使用 new 关键字。所以第一条语句将创建一个 HandleScope 实例,该实例将存储在 handle_scope 中。 现在我不明白第二个语句是如何工作的。据我所知, = 之前的部分将创建一个新的持久对象,该对象可以由变量上下文引用。那么 Context::New() 会创建一个新对象并将其存储在上下文中吗?呵呵,我知道我错了。但我根本不明白它是如何工作的?

我正在尝试为上述内容编写一个 C++ 类。这是我的尝试。

class MyClass {
private:
    HandleScope handle_scope;
    Persistent<Context> context;
    Context::Scope context_scope;

public:
    MyClass(); 
};

MyClass::MyClass()
{
    context = Context::New();
    context_scope = new Context::Scope(context);
}

我是否正确完成了初始化?

编辑:回复 peachykeen(在 cmets 中) 我做了以下实验。

我写了一个如下的测试类。 测试 { 上市: 测试() { cout

在主函数中我写了Test test;它输出“Test”,这意味着在不使用 new 关键字的情况下创建了一个对象。

【问题讨论】:

  • @peachykeen:不,请不要说那样的话。周围有足够多的糟糕 C++ 程序员,却没有积极推广不良做法。您确实不需要需要在 C++ 中用new 乱扔代码,这样做只是询问内存泄漏(这也是一个糟糕的 C++ 程序员的明显标志)
  • @jalf 以及如何在没有新的情况下使用智能指针? oO
  • @peachykeen:谁在乎他们内部使用什么。使用内部调用newdelete 的数据结构与在高级代码中临时调用newdelete 之间存在巨大差异。如果我在 RAII 类之外看到 delete 调用,我知道代码已损坏。如果我看到 new 调用,其结果没有立即传递给智能指针,我怀疑代码已损坏。而且如果我看到很多new调用,不管结果是否传递给智能指针,我都知道代码写得不好
  • @peachykeen 我只是按照你所说的去做。您说“最肯定”确实需要在 C++ 中使用new,而事实上,它可以而且应该经常避免。您说 Java 和 C++ 在创建对象的方式上“相似”,但事实并非如此,因为在 C++ 中,您通常应该在不使用 new 的情况下创建对象,并且您说 C++ 要求您销毁对象明确地,这仅适用于编写不当的 C++ 代码,它过度使用 new,并且无法使用 RAII。我不明白你的意思,但我不得不向像我一样阅读你评论的其他人发出警告
  • C++ 是一种危险的语言,如果你懒得去学习它。它不是“像 Java 和 C# 这样的 OO 语言”,这样对待它会导致很多痛苦和痛苦。 ;)

标签: c++


【解决方案1】:

你是对的,在 C++ 中,对象一被定义就被创建。您不需要使用new 关键字。

但是,与 Java 不同,对象可以创建具有不同类型的持续时间。使用new 在堆上创建一个对象,具有动态 存储持续时间:该变量一直存在,直到您明确地delete 它。 (并且new返回了一个指向创建对象的指针,这样就可以跟踪了)

如果您只是简单地定义一个对象,就像在您的第一行和第三行中一样,那么它会以自动 存储持续时间创建:也就是说,该对象会一直存在,直到超出范围为止。

这意味着您可以在函数内创建对象,并保证在您离开该函数时它们将立即被销毁——无论您如何离开功能。无论您是返回还是抛出异常,所有具有自动存储期限的对象(未使用new 创建)都保证被正确清理。

这意味着您应始终尽可能避免使用new。如果您必须使用new,您通常应该将生成的指针包装到一个智能指针 类中,这是一个使用自动存储持续时间创建的对象,以便它自动销毁)。然后,智能指针将自动在新分配的对象上调用delete,再次确保您不会泄漏内存。

这个区别是一个非常强大的工具,优秀的 C++ 程序员需要很好地理解它。它是避免内存泄漏的关键,更一般地说,是避免各种资源泄漏,并且在某些方面比 Java 的垃圾收集器更强大。

例如,假设我们希望打开一个文件,然后向其中写入一些数据。在 C++ 中,我们可以这样做:

void foo() {
    std::ofstream file("foo.txt");
    doStuff(file); // call a function which does something with the file   
}

并且因为file 是在没有使用new 的情况下声明的,因为它具有自动存储持续时间,我们保证它会在它运行时调用其析构函数超出范围,它会被正确清理——也就是说,流将被刷新,文件句柄将被关闭。

doStuff 是否会抛出异常并不重要。 无论如何我们离开foofile 将被正确销毁,所以我们不需要像在Java 中那样搞乱try/finally。该类本身是异常安全的,不需要用户做任何额外的工作。

尝试用 Java 编写一个类似的 sn-p,它保证即使 doStuff 抛出异常,文件也会立即关闭。它会更长,并且需要用户更加小心。

【讨论】:

  • 优秀的答案。我可能会指出,“带有智能指针的新”是 Java 对象工作方式的可接受近似值,但是使用 new 没有 智能指针在堆上创建对象和在堆栈上创建对象是两个@Cracker,你应该确保你理解的主题,因为它们在 Java 中没有等价物。
【解决方案2】:
Persistent<Context> context = Context::New();

创建 Persistent&lt;Context&gt; 类型的对象,如果 c-tor 不明确,则从 Context::New 返回的初始化。

简单的例子。

#include <iostream>

class C
{
public:
    C(int)
    {
        std::cout << "C::C(int)" << std::endl;
    }
};

int main()
{
    C c = 1;
}

你的班级应该是

class MyClass {
private:
    HandleScope handle_scope;
    Persistent<Context> context;
    Context::Scope context_scope;

public:
    MyClass(); 
};

MyClass::MyClass():context(Context::New()),
    context_scope(Context::Scope(context))
{
}

如果 Context::Scope 不是指针。

【讨论】:

    【解决方案3】:

    这将是等效的类:

    class MyClass {
    private:
        HandleScope handle_scope;
        Persistent<Context> context;
        Context::Scope context_scope;
    
    public:
        MyClass(); 
    };
    
    MyClass::MyClass()
    : context(Context::New()),
      context_scope(context)
    {
    }
    

    当你写这样的语句时:

    Persistent<Context> context = Context::New();
    

    您正在使用复制构造函数构造上下文。这与创建对象然后分配新值不同,尽管结果可能通常是相同的。

    同样的声明:

    Context::Scope context_scope(context);
    

    正在构造 context_scope 并将上下文传递给构造函数。您可以使用构造函数初始化器语法在类中获得等效行为,如我的示例所示。

    【讨论】:

      【解决方案4】:

      要创建一个对象的实例,你只需要这样:Type name; newkeyword 创建一个指向对象的指针。 通常,要初始化对象,我们使用括号:@ 987654323@ 有时,当对象支持复制时,您可以使用返回对象并将其分配给该对象的函数:Type name = Some_function_that_returns_Type(); 您现在可以像使用任何其他对象一样使用name。如果你说Type name = new Type;,你会得到一个编译器错误。 关键字 new 返回一个指针。Type * name = new Type; 是正确的。 (注意*,表示它是一个指向Type 类的指针,名为name。当我提到Type 时,它是任何对象,例如您的HandleScope。当我指的是name,它是你正在创建的新对象。所有的一切都是这样的:new是一个完全不同的关键字,指的是指针。如果你不使用指针,请不要使用它。使用基本格式Type name(parameter, another_param);

      【讨论】:

        猜你喜欢
        • 2017-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-25
        • 2017-10-28
        • 1970-01-01
        相关资源
        最近更新 更多