参考 c++ 上的默认析构函数行为
Default destructor behavior on reference c++
我在互联网上搜索过,但找不到任何关于默认析构函数行为的参考资源。
示例:
struct A{
int &a;
A(int&i): a(i){}
}
void f(){
int* i = new int;
A* a = new A(*i);
delete a; // is i been destroyed?
}
在这两种情况下,我在哪里可以找到相关资源?或者有人可以向我解释它的行为方式吗?
销毁引用不会执行任何操作 - 它不会销毁引用的对象。当您还记得我们传递对大多数方法的引用时,这一点就很清楚了。这些方法不会破坏引用的对象。指针是相同的。销毁指针不会销毁它指向的对象。
在你的例子中,i
指向的 int
被泄露了。
如果您修复代码以便编译,也许它会变得更清楚。您不能将指针传递给需要引用的方法。您需要先取消引用指针(在这种情况下这是一个误导性的表达)。
所以你实际上是在传递一个普通变量。
您可以对采用引用的函数执行的操作是传递一个从一开始就不是指针的变量。
如果你意识到这一点,就会更清楚析构函数销毁引用值是没有意义的。
它不能保证它是动态创建的并且需要被销毁。
(析构函数甚至 delete
不会自动成为指针成员。您需要明确地执行此操作。)
简而言之,它没有被销毁,你会发生内存泄漏。
现在的问题是您实际需要什么样的行为。对象所有权管理可能有不同的策略。最佳实践是使用智能指针而不是原始指针,至少对于所有权很重要的对象而言。例如,您可以使用 shared_ptr:
struct A{
std::shared_ptr<int> a;
A(std::shared_ptr<int> i): a(i){}
}
void f(){
std::shared_ptr<int> i(new int);
std::shared_ptr<A> a(new A(i));
}
这个例子开启了其他问题:i
指向的值是否应该用在其他地方?它应该在对象 a
中存活吗?
如果其他地方没有使用,您可以(并且应该)使用 unique_ptr
。不同的寿命管理策略可能采用其他模式。
引用不是对象。他们没有析构函数。他们有生命。与 C++ 中的任何其他类型一样,对象也有生命周期。
不同之处在于,当对象的生命周期结束时,将调用其析构函数。
但是当任何其他类型的生命周期结束时,它们的资源将被简单地释放。
在引用的情况下,它们持有的资源是指向另一个值的内存地址。不是价值本身。所以当它被释放时,价值保持不变。存储的内存地址是要释放的内容,而不是值。
当您使用 new/delete 时,您是在手动声明生命周期何时开始(新建)和何时结束(删除)。
在您的例子中,您的生命周期 X 和 Y 如下:
struct A{
int &a;
A(int&i): a(i){}
}
void f(){
int* i = new int; // ----------------------------------|X
A* a = new A(i); // -------------------|Y |X
delete a; // is i been destroyed? // --|Y |X
} // |X
// |X...
如您所见,X 的生命周期一直持续下去,因为您没有手动删除它,而是使用 new 创建了它。这意味着 i
中保留的值在您删除 a
.
的行之后仍然有效
PD:你应该写成 new A(*i)
,否则会出现编译错误。
我在互联网上搜索过,但找不到任何关于默认析构函数行为的参考资源。
示例:
struct A{
int &a;
A(int&i): a(i){}
}
void f(){
int* i = new int;
A* a = new A(*i);
delete a; // is i been destroyed?
}
在这两种情况下,我在哪里可以找到相关资源?或者有人可以向我解释它的行为方式吗?
销毁引用不会执行任何操作 - 它不会销毁引用的对象。当您还记得我们传递对大多数方法的引用时,这一点就很清楚了。这些方法不会破坏引用的对象。指针是相同的。销毁指针不会销毁它指向的对象。
在你的例子中,i
指向的 int
被泄露了。
如果您修复代码以便编译,也许它会变得更清楚。您不能将指针传递给需要引用的方法。您需要先取消引用指针(在这种情况下这是一个误导性的表达)。 所以你实际上是在传递一个普通变量。
您可以对采用引用的函数执行的操作是传递一个从一开始就不是指针的变量。
如果你意识到这一点,就会更清楚析构函数销毁引用值是没有意义的。
它不能保证它是动态创建的并且需要被销毁。
(析构函数甚至 delete
不会自动成为指针成员。您需要明确地执行此操作。)
简而言之,它没有被销毁,你会发生内存泄漏。
现在的问题是您实际需要什么样的行为。对象所有权管理可能有不同的策略。最佳实践是使用智能指针而不是原始指针,至少对于所有权很重要的对象而言。例如,您可以使用 shared_ptr:
struct A{
std::shared_ptr<int> a;
A(std::shared_ptr<int> i): a(i){}
}
void f(){
std::shared_ptr<int> i(new int);
std::shared_ptr<A> a(new A(i));
}
这个例子开启了其他问题:i
指向的值是否应该用在其他地方?它应该在对象 a
中存活吗?
如果其他地方没有使用,您可以(并且应该)使用 unique_ptr
。不同的寿命管理策略可能采用其他模式。
引用不是对象。他们没有析构函数。他们有生命。与 C++ 中的任何其他类型一样,对象也有生命周期。
不同之处在于,当对象的生命周期结束时,将调用其析构函数。 但是当任何其他类型的生命周期结束时,它们的资源将被简单地释放。
在引用的情况下,它们持有的资源是指向另一个值的内存地址。不是价值本身。所以当它被释放时,价值保持不变。存储的内存地址是要释放的内容,而不是值。
当您使用 new/delete 时,您是在手动声明生命周期何时开始(新建)和何时结束(删除)。
在您的例子中,您的生命周期 X 和 Y 如下:
struct A{
int &a;
A(int&i): a(i){}
}
void f(){
int* i = new int; // ----------------------------------|X
A* a = new A(i); // -------------------|Y |X
delete a; // is i been destroyed? // --|Y |X
} // |X
// |X...
如您所见,X 的生命周期一直持续下去,因为您没有手动删除它,而是使用 new 创建了它。这意味着 i
中保留的值在您删除 a
.
PD:你应该写成 new A(*i)
,否则会出现编译错误。