C++ 在c++;

C++ 在c++;,c++,memory-management,pointers,C++,Memory Management,Pointers,编辑:我知道在这种情况下,如果它是一个实际的类,我最好不要将字符串放在堆上。然而,这只是一个示例代码,以确保我理解该理论。实际代码将是一棵红黑树,所有节点都存储在堆上 在继续之前,我希望确保这些基本思想是正确的(我来自Java/Python背景)。我一直在网上搜索,但还没有找到这个问题的具体答案 当您重新分配指向新对象的指针时,是否必须首先对旧对象调用delete以避免内存泄漏?我的直觉告诉我是的,但在继续之前我需要一个具体的答案 例如,假设有一个类存储了指向字符串的指针 class MyCla

编辑:我知道在这种情况下,如果它是一个实际的类,我最好不要将字符串放在堆上。然而,这只是一个示例代码,以确保我理解该理论。实际代码将是一棵红黑树,所有节点都存储在堆上

在继续之前,我希望确保这些基本思想是正确的(我来自Java/Python背景)。我一直在网上搜索,但还没有找到这个问题的具体答案

当您重新分配指向新对象的指针时,是否必须首先对旧对象调用delete以避免内存泄漏?我的直觉告诉我是的,但在继续之前我需要一个具体的答案

例如,假设有一个类存储了指向字符串的指针

class MyClass
{
private:
    std::string *str;

public:
MyClass (const std::string &_str)
{
    str=new std::string(_str);
}

void ChangeString(const std::string &_str)
{
    // I am wondering if this is correct?
    delete str;
    str = new std::string(_str)

    /*
     * or could you simply do it like:
     * str = _str;
     */ 
}
....
在ChangeString方法中,哪一个是正确的

我想如果你不在第二种方式中使用new关键字,我会挂断电话,它仍然会像你期望的那样编译和运行。这是否只是覆盖了指针指向的数据?或者它还有别的作用吗

任何建议都将不胜感激:D

三点意见:

你也需要一个析构函数

~MyClass() 
{
    delete str;
}

在这种情况下,实际上不需要使用堆分配的内存。您可以执行以下操作:

class MyClass {
    private: 
        std::string str;

    public:
        MyClass (const std::string &_str) {
            str= _str;
        }

        void ChangeString(const std::string &_str) {
            str = _str;
};


您不能执行已注释掉的版本。那将是内存泄漏。Java处理这个问题是因为它有垃圾收集。C++没有这个特性。

为什么你认为你需要在类中存储一个指向字符串的指针?指向C++集合的指针(如字符串)实际上很少需要。几乎可以肯定,您的班级应该是这样的:

class MyClass
{
private:
    std::string str;

public:
MyClass (const std::string & astr) : str( astr )
{
}

void ChangeString(const std::string & astr)
{
    str = astr;
}
....
};
当您重新分配指向新对象的指针时,是否必须首先对旧对象调用delete以避免内存泄漏?我的直觉告诉我是的,但在继续之前我需要一个具体的答案

对。如果是原始指针,则必须先删除旧对象


当您分配一个新值时,有一些智能指针类可以为您执行此操作。

只需在此处指出,但是

str = _str;
无法编译(您试图将_str(通过引用传递的字符串的值)分配给str(字符串的地址)。如果你想这样做,你可以写:

str = &_str;
(您必须更改_str或str,以便constnest匹配)

但是,正如你的直觉告诉你的那样,你会泄露str已经指向的任何字符串对象的内存

正如前面指出的,当你在C++中添加一个变量给你的时候,你必须考虑变量是由对象拥有还是由其他东西拥有。p> 如果它是由对象拥有的,那么您最好将它存储为一个值,并复制周围的内容(但是您需要确保复制不会发生在您的后面)

它是不被拥有的,那么你可以将它存储为一个指针,并且你不一定需要一直复制东西

其他人会比我更好地解释这一点,因为我真的无法接受。 我最后经常做的是编写这样的代码:

class Foo {

private :
   Bar & dep_bar_;
   Baz & dep_baz_;

   Bing * p_bing_;

public:
   Foo(Bar & dep_bar, Baz & dep_baz) : dep_bar_(dep_bar), dep_baz_(dep_baz) {
       p_bing = new Bing(...);
   }

   ~Foo() {
     delete p_bing;
   }
也就是说,如果一个对象依赖于“Java”/“Ioc”意义上的某个对象(这些对象存在于其他地方,您不是在创建它,您只想对其调用方法),我将使用dep_xxxx将依赖项存储为引用

如果我创建对象,我将使用带有p_uu前缀的指针

这只是为了让代码更“即时”。我不确定这是否有用

就我的2c

祝你在内存管理方面好运,你是对的,它是来自Java的棘手部分;在你适应之前不要写代码,否则你会花很多时间去追逐塞戈尔特


希望这有帮助 如果您来自垃圾收集语言的背景,并且发现自己确实需要使用堆内存,我建议使用boost共享指针。您可以这样使用它们:

#include <boost/shared_ptr.hpp>
...
boost::shared_ptr<MyClass> myPointer = boost::shared_ptr<MyClass>(new MyClass());
#包括
...
boost::shared_ptr myPointer=boost::shared_ptr(new MyClass());

myPointer的语言语义与常规指针几乎相同,但shared_ptr使用引用计数来确定何时删除它引用的对象。基本上是自己动手的垃圾收集。文档如下:

如果必须取消分配旧实例并创建另一个实例,则应首先确保创建新对象成功:

void reset(const std::string& str)
{
    std::string* tmp = new std::string(str);
    delete m_str;
    m_str = tmp;
}
如果您先调用delete,然后创建一个新实例会抛出一个异常,那么类实例将留下一个悬空指针。例如,析构函数可能会再次尝试删除指针(未定义的行为)

您还可以通过将指针设置为NULL来避免这种情况,但上面的方法更好:如果重置失败,对象将保留其原始值


至于代码注释中的问题

*str = _str;
这将是正确的做法。这是正常的字符串赋值

str = &_str;

这将是分配指针,并且是完全错误的。您将泄漏先前由
str
指向的字符串实例。更糟糕的是,传递给函数的字符串很可能首先没有分配
new
(不应该混合指向动态分配对象和自动对象的指针)。此外,您可能正在存储一个字符串对象的地址,该对象的生存期以函数调用结束(如果const引用绑定到一个临时对象)。

我将为您编写一个类

class A
{
     Foo * foo;   // private by default
 public:
     A(Foo * foo_): foo(foo_) {}
     A(): foo(0) {}   // in case you need a no-arguments ("default") constructor
     A(const A &a):foo(new Foo(a.foo)) {}   // this is tricky; explanation below
     A& operator=(const &A a) { foo = new Foo(a.foo); return *this; }
     void setFoo(Foo * foo_) { delete foo; foo = foo_; }
     ~A() { delete foo; }
}
对于包含这样的资源的类,复制构造函数assignment oper