在下面的C++代码中,我可以显式调用析构函数而不是构造函数.这是为什么?不明确的ctor调用与dtor案件更具表现力和统一性吗?
class X { };
int main() {
X* x = (X*)::operator new(sizeof(X));
new (x) X; // option #1: OK
x->X(); // option #2: ERROR
x->~X();
::operator delete(x);
}
Run Code Online (Sandbox Code Playgroud) c++ constructor destructor placement-new explicit-destructor-call
在C++编程语言第4版中,有一个向量实现的示例,请参阅消息末尾的相关代码.
uninitialized_move()通过从旧内存区域移动新T对象将其初始化到新内存区域.然后它调用原始T对象上的析构函数,即移动对象.为什么在这种情况下需要析构函数调用?
这是我的不完全理解:移动对象意味着移动对象拥有的资源的所有权被转移到移动对象.移动对象中的剩余部分是一些不需要销毁的内置类型的可能成员,当vector_base b超出范围时(在reserve ()调用之后,它们将被释放)).移动对象中的所有指针都将被置于nullptr或者使用某种机制来删除这些资源上移动对象的所有权以便我们安全,那么为什么在"vector_base b"时调用耗尽对象上的析构函数"在交换完成后,析构函数仍会解除内存的释放?
我理解在必须调用析构函数时需要显式调用析构函数,因为我们有一些东西需要破坏(例如drop元素)但是在vector_base的std :: move + deallocation之后我看不到它的含义.我在网上阅读了一些文本,我看到被移动对象的析构函数调用作为对象生命周期结束的信号(对谁或什么?).
请告诉我,析构函数还有哪些有意义的工作要做?谢谢!
下面的代码片段来自http://www.stroustrup.com/4th_printing3.html
template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc)
{
if (newalloc<=capacity()) return; // never decrease allocation
vector_base<T,A> b {vb.alloc,size(),newalloc-size()}; // get new space
uninitialized_move(vb.elem,vb.elem+size(),b.elem); // move elements
swap(vb,b); // install new base
} // implicitly release old space
template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
using T = Value_type<Out>; // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_) …Run Code Online (Sandbox Code Playgroud) c++ memory-management move-semantics c++11 explicit-destructor-call
我有这个代码:
struct data {
void doNothing() {}
};
int main() {
data* ptr = new data();
ptr->~data();
ptr->doNothing();
::operator delete(ptr);
}
Run Code Online (Sandbox Code Playgroud)
请注意,doNothing()在对象被销毁之后但在其内存被释放之前被调用.看起来"对象生命周期"已经结束,但指针仍然指向正确分配的内存.成员函数不访问任何成员变量.
在这种情况下,成员函数调用是否合法?
c++ destructor object-lifetime language-lawyer explicit-destructor-call
我偶然发现了以下代码片段:
#include <iostream>
#include <string>
using namespace std;
class First
{
string *s;
public:
First() { s = new string("Text");}
~First() { delete s;}
void Print(){ cout<<*s;}
};
int main()
{
First FirstObject;
FirstObject.Print();
FirstObject.~First();
}
Run Code Online (Sandbox Code Playgroud)
该文本表示此代码段应该导致运行时错误.现在,我对此并不十分肯定,所以我尝试编译并运行它.有效.奇怪的是,尽管所涉及的数据非常简单,但在打印"文本"之后程序结结巴巴,并且仅在一秒钟之后完成.
我添加了一个要打印到析构函数的字符串,因为我不确定显式调用这样的析构函数是否合法.程序打印两次字符串.所以我的猜测是析构函数被调用两次,因为正常的程序终止不知道显式调用并试图再次销毁对象.
一个简单的搜索确认显式调用自动化对象上的析构函数是危险的,因为第二次调用(当对象超出范围时)具有未定义的行为.所以我很幸运,我的编译器(VS 2017)或这个特定的程序.
关于运行时错误,文本是否完全错误?或者运行时错误真的很常见吗?或者也许我的编译器实现了某种针对这类事情的warding机制?
c++ destructor lifetime undefined-behavior explicit-destructor-call
我知道显式调用析构函数会因为双析构函数调用而导致未定义的行为,如下所示:
#include <vector>
int main() {
std::vector<int> foo(10);
foo.~vector<int>();
return 0; // Oops, destructor will be called again on return, double-free.
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我们将新位置称为"复活"对象,该怎么办?
#include <vector>
int main() {
std::vector<int> foo(10);
foo.~vector<int>();
new (&foo) std::vector<int>(5);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
更正式的:
new)然后,在该对象被破坏之前,在其上调用placement new以"恢复"它?示例用例(虽然这个问题更多是关于好奇心):我想"重新分配"一个没有的对象operator=.
我已经看到这个问题,说"覆盖"具有非静态const成员的对象是非法的.所以,让我们将这个问题的范围限制在没有任何const成员的对象上.
请考虑以下代码:
#include <iostream>
typedef int t;
t a=42;
int main()
{
a.t::~t();
std::cout << a; //42
}
Run Code Online (Sandbox Code Playgroud)
我预计a会被摧毁.但事实并非如此,为什么?伪析构函数调用将如何销毁该对象?
c++ destructor primitive-types language-lawyer explicit-destructor-call
这里的"简单",我指的是一个非虚拟空析构函数或POD类型的类.
典型例子:
char buffer[SIZE];
T *p = new(buffer) T;
...
p->~T(); // <---- always ?
Run Code Online (Sandbox Code Playgroud)
如果我们不调用显式析构函数会发生什么p?我不认为这是未定义的行为或内存泄漏.
重用有什么问题buffer吗?
我有类数据,可以保存指向对象的指针.我希望以后能够手动调用它的析构函数,为此我需要将其地址存储在变量中,但似乎禁止使用构造函数/析构函数的地址.有没有办法解决 ?
struct Data {
union {
long i;
float f;
void* data_ptr;
} _data;
std::type_index _typeIndex;
void (*_destructor_ptr)();
template<typename T>
void Init() {
if constexpr (std::is_integral<T>::value) {
//
}
else if constexpr (std::is_floating_point<T>::value) {
//
}
else {
_data.data_ptr = new T;
_typeIndex = std::type_index(typeid(T));
_destructor_ptr = &T::~T; // << -- can't do this
}
}
Run Code Online (Sandbox Code Playgroud) 对于普通对象(甚至是对象const),允许通过显式调用析构函数来结束它们的生命周期。稍后,例如,程序可以使用放置在同一内存位置启动另一个对象生命周期new。
但是调用constexpr对象的析构函数合法吗?它能产生一些有用的或至少是结构良好的程序吗?
很容易想象相反的情况:
struct A{
int v = 0;
constexpr void foo() { v ? throw 1 : ++v; }
constexpr ~A() { foo(); }
};
constexpr A y;
int main() { y.~A(); y.~A(); }
Run Code Online (Sandbox Code Playgroud)
这个(很可能是格式错误的)程序被所有编译器接受,没有任何警告:https : //gcc.godbolt.org/z/aqMbfjxKT
而在锵它完成了从抛出异常constexpr的析构函数A。
我有一个包含unique_ptr相互依赖的字段的类:
class ResourceManager {
ResourceManager() {}
ResourceManager(A* a_ptr) :
b_ptr(new B(a)),
c_ptr(new C(b_ptr.get())) {}
ResourceManager& operator=(ResourceManager&& that) {
// Call destructor, then construct a new instance on top
~ResourceManager();
ResourceManager* new_this = new(this) ResourceManager();
// Surely this must be the case, right?
// Is there any reason to prefer using either?
assert(new_this == this);
new_this->b_ptr = that.b_ptr;
new_this->c_ptr = that.c_ptr;
return *new_this;
}
unique_ptr<B> b;
unique_ptr<C> c;
};
Run Code Online (Sandbox Code Playgroud)
这里的用例是我想将新值重新分配给指针,同时保持ResourceManager作为堆栈分配的变量,或者作为非指针类成员.
使用我当前的设置,我想像使用这样的东西:
A a, another_a;
ResourceManager …Run Code Online (Sandbox Code Playgroud)