C++ 为什么应该/不应该';我不能使用;新";运算符来实例化一个类,为什么?

C++ 为什么应该/不应该';我不能使用;新";运算符来实例化一个类,为什么?,c++,class,instantiation,C++,Class,Instantiation,我知道这可能会被理解为“你喜欢什么”的问题之一,但我真的想知道为什么你会选择以下方法中的一种而不是另一种 假设您有一个超级复杂的类,例如: class CDoSomthing { public: CDoSomthing::CDoSomthing(char *sUserName, char *sPassword) { //Do somthing... } CDoSomthing::~CDoSomt

我知道这可能会被理解为“你喜欢什么”的问题之一,但我真的想知道为什么你会选择以下方法中的一种而不是另一种

假设您有一个超级复杂的类,例如:


class CDoSomthing {

    public:
        CDoSomthing::CDoSomthing(char *sUserName, char *sPassword)
        {
            //Do somthing...
        }

        CDoSomthing::~CDoSomthing()
        {
            //Do somthing...
        }
};
如何在全局函数中声明本地实例


int main(void)
{
    CDoSomthing *pDoSomthing = new CDoSomthing("UserName", "Password");

    //Do somthing...

    delete pDoSomthing;
}
--或--


首选局部变量,除非需要对象的生存期扩展到当前块之外。(局部变量是第二个选项)。这比担心内存管理简单多了

另外,如果您需要一个指针,因为您需要它来传递给另一个函数,只需使用运算符的地址:

SomeFunction(&DoSomthing);

如果引发异常,第二个版本将释放堆栈。第一个不会。否则我看不出有什么不同。

Mark Ransom是正确的,如果要将变量作为参数传递给CreateThread-esque函数,还需要使用
new
实例化。

第二种形式是所谓的RAII(资源获取是初始化)模式。与第一种方法相比,它有许多优点

使用
new
时,您必须自己使用
delete
,并保证它将始终被删除,即使引发异常。你必须自己保证这一切

如果使用第二种形式,当变量超出范围时,它总是自动清除。如果抛出异常,堆栈将展开,并将其清理


因此,您应该更喜欢RAII(第二个选项)。

当您在堆栈上声明变量时,与在堆中声明变量时,有两个主要考虑因素-生存期控制和资源管理

当您严格控制对象的生命周期时,在堆栈上进行分配非常有效。这意味着您不会将该对象的指针或引用传递给本地函数范围之外的代码。这意味着,没有输出参数,没有COM调用,没有新线程。有很多限制,但是在从当前作用域正常或异常退出时,您可以正确地清理对象(不过,您可能需要使用虚拟析构函数读取堆栈展开规则)。堆栈分配的最大缺点是:堆栈通常被限制为4K或8K,因此您可能需要小心放置在堆栈上的内容

另一方面,在堆上进行分配需要手动清理实例。这也意味着您可以自由地控制实例的生命周期。您需要在两种情况下执行此操作:a)将该对象传递到范围之外;或者b)对象太大,在堆栈上分配它可能会导致堆栈溢出


顺便说一句,这两者之间的一个很好的折衷办法是在堆上分配对象,并在堆栈上分配指向它的智能指针。这可确保您不会浪费宝贵的堆栈内存,同时仍能在作用域退出时进行自动清理。

除了到目前为止所说的之外,还需要考虑其他性能因素,特别是在内存分配密集型应用程序中:

  • 使用
    new
    将从堆中分配内存。在密集(极其频繁)分配和解除分配的情况下,您将在以下方面付出高昂的代价:
    • 锁定:堆是进程中所有线程共享的资源。堆上的操作可能需要锁定堆管理器(在运行时库中为您完成),这可能会显著降低速度
    • 碎片:堆碎片。您可能会看到malloc/new和free/delete返回的时间增加了10倍。这加剧了上述锁定问题,因为管理碎片堆需要更多时间,更多线程排队等待修复锁定。(在Windows上,您可以为堆管理器设置一个特殊标志,以便它试探性地尝试减少碎片。)
  • 使用RAII模式,只需从堆栈中取出内存。堆栈是每个线程的资源,它不分段,不涉及锁定,并且可能在内存位置(即CPU级别的内存缓存)方面发挥优势

  • 因此,当您在短时间内需要对象时,一定要使用第二种方法(堆栈上的局部变量)。如果您需要在线程之间共享数据,请使用
    new/malloc
    (一方面,您必须这样做,另一方面,这些对象通常寿命足够长,因此相对于堆管理器,您基本上只需支付0成本。)

    两者之间的最大区别在于,新对象会启动指向该对象的指针

    通过创建不带new的对象,启动的对象存储在堆栈上。如果使用new启动,它将返回指向堆上创建的新对象的指针。它实际上返回指向新对象的内存地址。发生这种情况时,您需要对变量进行内存管理。使用var如果可以,您需要对其调用delete以避免内存泄漏。如果没有新的运算符,当变量超出范围时,内存将自动释放


    因此,如果您需要将变量传递到当前作用域之外,则使用new更有效。但是,如果您需要创建临时变量或仅临时使用的对象,将对象放在堆栈上会更好,因为您不必担心内存管理。

    第二个选项的作用是什么?@Reyes,是的。您的第二个示例依赖于RAII。在这种情况下,这并不重要(除非您调用main…),但对于许多其他情况,异常处理是指针的问题,依赖于RAII更安全。我也有同样的感觉,不必担心内存管理,但我是那种“老派”之一开发人员只需要看到匹配的f
    SomeFunction(&DoSomthing);