当前位置:  开发笔记 > 编程语言 > 正文

我为什么/不应该使用"new"运算符来实例化一个类,为什么?

如何解决《我为什么/不应该使用"new"运算符来实例化一个类,为什么?》经验,为你挑选了4个好方法。

我理解这可能被解释为"你的偏好是什么"问题之一,但我真的想知道为什么你会选择以下方法之一而不是另一种方法.

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

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;
}

- 要么 -

int main(void)
{
    CDoSomthing DoSomthing("UserName", "Password");

    //Do somthing...

    return 0;
}

Mark Ransom.. 27

首选局部变量,除非您需要将对象的生命周期延伸到当前块之外.(局部变量是第二种选择).这比担心内存管理更容易.

PS如果你需要一个指针,因为你需要它传递给另一个函数,只需使用address-of运算符:

SomeFunction(&DoSomthing);

NTDLS:这不是老派,它只是*非常糟糕的C++实践.这意味着,如果您使用RAII,那么只有*不可能发生一整类令人讨厌的错误.在C中,你显然没有选择.这就是为什么C++不应该被视为C类的原因. (8认同)

或者,除非你在一个小堆栈的环境中工作(我正在看着你,Windows CE ...) (4认同)


Franci Penov.. 22

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

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

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

顺便说一下,这两者之间的一个很好的折衷方案是在堆上分配对象并在堆栈上为它分配一个智能指针.这可以确保您不会浪费宝贵的堆栈内存,同时仍然可以在范围退出时进行自动清理.



1> Mark Ransom..:

首选局部变量,除非您需要将对象的生命周期延伸到当前块之外.(局部变量是第二种选择).这比担心内存管理更容易.

PS如果你需要一个指针,因为你需要它传递给另一个函数,只需使用address-of运算符:

SomeFunction(&DoSomthing);


NTDLS:这不是老派,它只是*非常糟糕的C++实践.这意味着,如果您使用RAII,那么只有*不可能发生一整类令人讨厌的错误.在C中,你显然没有选择.这就是为什么C++不应该被视为C类的原因.
或者,除非你在一个小堆栈的环境中工作(我正在看着你,Windows CE ...)

2> Franci Penov..:

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

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

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

顺便说一下,这两者之间的一个很好的折衷方案是在堆上分配对象并在堆栈上为它分配一个智能指针.这可以确保您不会浪费宝贵的堆栈内存,同时仍然可以在范围退出时进行自动清理.



3> R. Martinho ..:

第二种形式是所谓的RAII(资源获取是初始化)模式.它比第一个有许多优点.

使用时new,您必须delete自己使用,并保证它将永远被删除,即使抛出异常.你必须自己保证这一切.

如果使用第二种形式,当变量超出范围时,它将始终自动清除.如果抛出异常,堆栈会展开并清除它.

所以,你应该更喜欢RAII(第二种选择).



4> vladr..:

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

    使用new将从堆中分配内存.在激烈(极其频繁)分配和解除分配的情况下,您将付出高昂的代价:

    锁定:堆是进程中所有线程共享的资源.堆上的操作可能需要在堆管理器中锁定(在运行时库中为您完成),这可能会显着降低速度.

    碎片:堆碎片.您可能会看到malloc/new和free/delete返回的时间增加了10倍.这与上面的锁定问题有关,因为它需要更多的时间来管理碎片堆,并且更多线程排队等待愈合锁定.(在Windows上,您可以为堆管理器设置一个特殊标志,以便启发式地尝试减少碎片.)

    使用RAII模式,内存简单地从堆栈中取出.堆栈是一个每线程资源,它没有碎片,没有涉及锁定,并且可能在内存局部性方面发挥优势(即CPU级别的内存缓存).

因此,当您需要短暂(或范围)时间段的对象时,一定要使用第二种方法(局部变量,在堆栈上.)如果您需要在线程之间共享数据,请使用new/malloc(一方面,您必须,另一方面,这些对象通常具有足够长的寿命,因此与堆管理器相比,您支付的成本基本上是0.)

推荐阅读
重庆制造漫画社
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有