gpt4 book ai didi

c++ - 如果将变量设置为新对象,旧对象会发生什么情况?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:26:53 26 4
gpt4 key购买 nike

假设我们有一个类 X,它没有有一个重载的 operator=() 函数。

class X {
int n;

X() {n = 0;}
X(int _n) {n = _n;}
};

int main() {
X a; // (1) an object gets constructed here

// more code...

a = X(7); // (2) another object gets constructed here (?)

// some more code...

a = X(12); // (3) yet another object constructed here (?)
return 0;
}

是否在(2)处构造了一个新对象?如果是,在 (1) 处构造的旧对象会发生什么情况?它是自动销毁还是释放(是哪个)?是否被覆盖?

在代码 (3) 的下方发生了什么?

最重要的是,是否有可能通过编写上述代码导致内存泄漏?

最佳答案

您需要了解的是,作为新手,您不知道编译器生成的许多“隐式”代码。我们将使用您的 class X 代码作为直接示例:

class X {
int n;
public: //You didn't include this, but this won't work at all unless your constructors are public
X() {n = 0;}
X(int _n) {n = _n;}
};

在代码变成目标代码之前,但在你的编译器得到你的类定义之后,它会将你的类转换成看起来(大致)像这样的东西:

class X {
int n;
public:
X() {n = 0;} //Default-Constructor
X(int _n) {n = _n;} //Other Constructor
//GENERATED BY COMPILER
X(X const& x) {n = x.n;} //Copy-Constructor
X(X && x) {n = x.n;} //Move-Constructor
X & operator=(X const& x) {n = x.n; return *this;} //Copy-Assignment
X & operator=(X && x) {n = x.n; return *this;} //Move-Assignment
~X() noexcept {} //Destructor
};

关于何时自动创建这些成员的规则并不是很明显 (A good starting reference here),但现在,您可以相信,在这种情况下,这正是发生的事情。

因此,在您的 main 函数中,让我们回顾一下发生了什么,并通过注释引起注意的细节:

int main() {
X a; //Default-Constructor called
a = X(7);//Other Constructor called, then Move-Assignment operator called,
//then Destructor called on temporary created by `X(7)`
a = X(12); //Same as previous line

return 0;
//Destructor called on `a`
}

我们将添加更多行来显示这些调用的大部分(如果不是全部)各种排列:

int main() {
X a; //Default-Constructor
X b = a; //Copy-Constructor (uses copy-elision to avoid calling Default + copy-assign)
X c(5); //Other Constructor
X d{7}; //Also Other Constructor
X e(); //Declares a function! Probably not what you intended!
X f{}; //Default-Constructor
X g = X(8); //Other Constructor (uses copy-elision to avoid calling Other + move-assign + Destructor)
X h = std::move(b); //Move-Constructor (uses copy-elision to avoid calling Default + move-assign)
b = c; //Copy-assignment
b = std::move(d); //Move-assignment
d = X{15}; //Other Constructor, then Move-Assignment, then Destructor on `X{15}`.
//e = f; //Will not compile because `e` is a function declaration!
return 0;
//Destructor on `h`
//Destructor on `g`
//Destructor on `f`
//Destructor will NOT be called on `e` because `e` was a function declaration,
//not an object, and thus has nothing to clean up!
//Destructor on `d`
//Destructor on `c`
//Destructor on `b`
//Destructor on `a`
}

这应该涵盖基础知识。

And most importantly, is there ever a chance of causing a memory leak by writing code like in the above?

正如所写,没有。但是,假设您的类(class)做了这样的事情:

class X {
int * ptr;
public:
X() {
ptr = new int{0};
}
};

现在,您的代码会泄漏,因为每次创建 X 时,您都会有一个永远不会被删除的指针。

要解决这个问题,您需要确保 A) 析构函数正确清理指针,以及 B) 您的复制/移动构造函数/运算符是正确的。

class X {
int * ptr;
public:
X() {
ptr = new int{0};
}
X(int val) {
ptr = new int{val};
}
X(X const& x) : X() {
*ptr = *(x.ptr);
}
X(X && x) : X() {
std::swap(ptr, x.ptr);
}
X & operator=(X const& x) {
*ptr = *(x.ptr);
return *this;
}
X & operator=(X && x) {
std::swap(ptr, x.ptr);
return *this;
}
~X() noexcept {
delete ptr;
}
};

如果按原样在您的main 函数或我的函数中使用,此代码将不会泄漏内存。但是,当然,如果您这样做并不能阻止泄漏:

int main() {
X * ptr = new X{};
return 0;
//Whelp.
}

一般来说,如果您根本不需要使用指针,建议您改用 std::unique_ptr 之类的东西,因为它免费提供了大部分内容。

int main() {
std::unique_ptr<X> ptr{new X{}};
return 0;
//Destructor called on *ptr
//`delete` called on ptr
}

这在您的原始类(class)中是个好主意,但需要注意的是,除非您明确更改它,否则您的类(class)将不再可复制(尽管它仍然是可移动的):

class X {
std::unique_ptr<int> ptr;
public:
X() {
ptr.reset(new int{0});
}
X(int val) {
ptr.reset(new int{val});
}
//X(X && x); //auto generated by compiler
//X & operator=(X && x); //auto generated by compiler
//~X() noexcept; //auto generated by compiler

//X(X const& x); //Deleted by compiler
//X & operator=(X const& x); //Deleted by compiler
};

我们可以看到我以前版本的main的变化:

int main() {
X a; //Default-Constructor
//X b = a; //Was Copy-Constructor, no longer compiles
X c(5); //Other Constructor
X d{7}; //Also Other Constructor
X f{}; //Default-Constructor
X g = X(8); //Other Constructor (uses copy-elision to avoid calling Other + move-assign + Destructor)
X h = std::move(c); //Move-Constructor (uses copy-elision to avoid calling Default + move-assign)
//b = c; //Was Copy-assignment, no longer compiles
c = std::move(d); //Move-assignment
d = X{15}; //Other Constructor, then Move-Assignment, then Destructor on `X{15}`.
return 0;
//Destructor on `h`
//Destructor on `g`
//Destructor on `f`
//Destructor on `d`
//Destructor on `c`
//Destructor on `a`
}

如果你想使用 std::unique_ptr,但也希望生成的类是可复制的,你需要使用我讨论的技术自己实现复制构造函数。

应该就这些了!如果我遗漏了什么,请告诉我。

关于c++ - 如果将变量设置为新对象,旧对象会发生什么情况?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44707245/

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