因此,我有一个称为point的类,每次构造和销毁对象时,它仅登录到控制台。我做了以下工作:
#include <iostream>
struct point{
point() {
std::cout << "ctor\n";
}
~point() {
std::cout << "dtor\n";
}
};
int main(){
int x = 3;
point* ptr = new point[x]{point()};
delete[] ptr;
}
ctor
dtor
dtor
dtor
最终只调用了一次构造函数,而两次调用了析构函数,为什么?我知道这很糟糕,可能是ub,但我想了解原因。其他分配给了我“预期的”输出:
int x = 3;
constexpr int y = 3;
point* ptr1 = new point[3];
point* ptr2 = new point[x];
point* ptr3 = new point[y]{point()};
ctor
ctor
ctor
dtor
dtor
dtor
我正在使用Visual Studio 19最新版本。
这是编译器错误。
通过使用操作符new而不使用常量定义的类型大小,MSVC编译器将在初始化器列表和/或数组大小中显式指定的多次调用类对象的构造函数和析构函数。
#include <iostream>
struct point {
point() {
std::cout << "ctor\n";
}
~point() {
std::cout << "dtor\n";
}
};
int main() {
int x = 3;
point* ptr = new point[x]{point()};
delete[] ptr;
}
如上所述,将被称为明确指定point
ctor一次。
这可以通过:point* ptr = new point[x]{point(), point()};
声明
ctor ctor dtor dtor dtor
。ctor ctor ctor dtor dtor dtor
(应保证)甚至是超出界限的异常UB的可抛出数组:point* ptr = new point[x]{point(), point(), point(), point(), point() };
都遵循该行为。
ctor ctor ctor ctor ctor dtor dtor dtor
。terminate called after throwing an instance of 'std::bad_array_new_length'
如果定义的大小为常数,则正确检测到太多初始化程序。即const int x = 3
或constexpr int x = 3
这可能不会给您直接的答案,而只是提出一些意见。
正如@interjay所评论的,我们可以看到,同一段代码在VC ++和其他流行的编译器中的运行方式有所不同。我没有看到MSDN中提到过任何与标准的偏离,事实上,VC ++确实支持the standards in VS2017之后的列表和其他别名。
现在,当使用不同的符号重写同一件事时,我观察到的东西很有趣。验证MSDN中记录的内容后,我看到以下表示法
point* ptr = new point[x]{};
输出符合预期,并且功能上在不同的编译器中保持一致。
ctor
ctor
ctor
dtor
dtor
dtor
也具有以下基于堆栈的数组分配,
point ptr[3] = {point()};
获取我们
ctor
ctor
ctor
dtor
dtor
dtor
到目前为止,一切看起来不错,并且结果与其他编译器匹配,直到我们调用以下代码:
point* ptr = new point[x]{point()};
仅从我所了解的最好的情况来看,这可能是VC ++中的实现故障。
当您调用new运算符以及大括号内的构造函数初始化时,可能不建议在VC ++中使用,并且在内部它会退回到某些自定义初始化处理程序中,在该处理程序中,当显式分配时应调用构造函数对于每个索引,在休息时,其他所有内容都只分配有未初始化的内存(不会调用任何构造函数)。这是明显的未定义行为(UB)。
虽然,在我展示过的两个示例中,看起来(看着Windbg中的调用栈,我可能完全错了),在运行时内部VC ++在调用类似于矢量构造函数初始化器的东西,后者调用了所有索引的默认构造函数(仅)迭代。
同样可以通过以下代码验证调用构造函数重载的方法
point(int i) {
std::cout << "ctor "<<i<<"\n";
}
来自
point ptr[3] = {point(10)}; //not a default constructor
吐出:
ctor 10
ctor
ctor
dtor
dtor
dtor
这很奇怪而且是异常行为,这在VC ++中已经不是什么新鲜事物了。我可以建议的是,尝试使用以下表示法在不同的编译器中获得一致的结果:
point* ptr = new point[x]{};
最终只调用了一次构造函数,而两次调用了析构函数,为什么?
您还需要记录副本并移动构造函数:
#include <iostream>
struct point{
point() {
std::cout << "default ctor\n";
}
point(const point &) {
std::cout << "copy ctor\n";
}
point(point &&) {
std::cout << "move ctor\n";
}
~point() {
std::cout << "dtor\n";
}
};