您可以在不调用构造函数的情况下调用攻击器吗?
Can you call the destructor without calling the constructor?
我一直在尝试在不需要时不初始化内存,并且正在使用malloc数组来做:
这就是我运行的:
#include <iostream>
struct test
{
int num = 3;
test() { std::cout << "Initn"; }
~test() { std::cout << "Destroyed: " << num << "n"; }
};
int main()
{
test* array = (test*)malloc(3 * sizeof(test));
for (int i = 0; i < 3; i += 1)
{
std::cout << array[i].num << "n";
array[i].num = i;
//new(array + i) i; placement new is not being used
std::cout << array[i].num << "n";
}
for (int i = 0; i < 3; i += 1)
{
(array + i)->~test();
}
free(array);
return 0;
}
输出:
0 ->- 0
0 ->- 1
0 ->- 2
Destroyed: 0
Destroyed: 1
Destroyed: 2
尽管没有构造数组索引。这是"健康"吗?也就是说,我可以简单地将破坏者视为"只是功能"吗?(除了驱动器对数据成员相对于我指定的指针的位置有隐式知识之外(
(只是指定:我不是在寻找有关C 适当使用的警告。我简单地想知道使用这种无构造方法时是否应该警惕。
(脚注:我不想使用构造函数的原因是因为很多次,内存根本不需要初始化,并且这样做很慢(
不,这是不确定的行为。对象的寿命在呼叫构造函数完成后开始,因此,如果从未调用构造函数,则该对象在技术上永远不会存在。
这种可能"似乎"在您的示例中表现正确,因为您的结构是微不足道的(int :: 〜int是一个no-op(。
您还泄漏了内存(驱动器破坏给定的对象,但是通过malloc
分配的原始内存仍然需要为free
D(。
编辑:您可能还想查看这个问题,因为这是一个非常相似的情况,只需使用堆栈分配而不是malloc
即可。这给出了围绕对象寿命和构造的标准中的一些实际报价。
我也会添加它:如果您不使用新的位置并且显然需要它(例如,struct包含一些容器类或VTable等(,您将遇到真正的麻烦。在这种情况下,省略安置新呼叫几乎可以肯定会为您带来0个非常脆弱的代码的绩效收益 - 无论哪种方式,这都是一个好主意。
是的,驱动器不过是功能。您可以随时调用它。但是,在没有匹配的构造函数的情况下称其为一个坏主意。
因此,该规则是:如果您未将内存初始化为特定类型,则可能不会解释和使用该内存作为该类型的对象;否则是未定义的行为。(用char
和unsigned char
作为例外(。
让我们通过对您的代码进行行分析进行行。
test* array = (test*)malloc(3 * sizeof(test));
此行使用系统提供的内存地址初始化指针标量array
。请注意,对于任何类型的类型,内存>不初始化。这意味着您不应将这些内存视为任何对象(即使像int
这样的标量,请撇开您的test
类类型(。
后来,您写道:
std::cout << array[i].num << "n";
这将内存作为test
类型,违反了上述规则,导致了不确定的行为。
及以后:
(array + i)->~test();
您再次使用了test
类型的内存!调用驱动器也使用对象!这也是ub。
在您的情况下,您很幸运,没有任何有害发生,并且您会得到合理的事情。但是,瑞银完全取决于您的编译器的实施。它甚至可以决定格式化您的磁盘,并且仍然是标准配合的。
也就是说,我可以简单地将驱动器视为"只是功能"?
否。尽管它在许多方面都像其他功能一样,但灾难有一些特殊功能。这些归结为类似于手动内存管理的模式。就像内存分配和交易需要成对的一样,建筑和破坏也是如此。如果跳过一个,请跳过另一个。如果您致电一个,请致电另一个。如果您坚持手动内存管理,那么施工和破坏的工具将是新的,并且明确地称为灾难。(使用new
和delete
的代码将分配和构造合并为一个步骤,而破坏和交易合并组合到另一个步骤中。(
不要跳过将要使用的对象的构造函数。这是不确定的行为。此外,构造函数越少,如果您跳过它,就越有可能出错。也就是说,随着您节省更多的时间,您会打破更多。跳过使用的对象的构造函数并不是提高效率&mdash的一种方式。这是编写破码的方法。效率低下,正确的代码胜过无效的高效代码。
一点点沮丧:这种低级管理可能会成为时间的巨大投资。只有在绩效回报的现实机会时才走这条路线。不要仅仅为了优化而将代码复杂化。还要考虑更简单的替代方案,这些替代方案可能会获得类似的结果,而较少的代码开销。也许除了以某种方式标记未初始化的对象外,没有执行初始化的构造函数?(细节和可行性取决于所涉及的类,因此扩展了这个问题的范围。(
一点点鼓励:如果您考虑标准库,则应该意识到自己的目标是可以实现的。我将提出vector::reserve
作为可以在不初始化内存的情况下分配内存的示例。
您当前在从不存在的对象访问字段时,您当前拥有UB。
您可以通过执行构造函数NOOP来使字段非初始化。然后,编译器可能很容易进行初始化,例如:
struct test
{
int num; // no = 3
test() { std::cout << "Initn"; } // num not initalized
~test() { std::cout << "Destroyed: " << num << "n"; }
};
演示
对于可读性,您可能应该将其包装在专门的类中,例如:
struct uninitialized_tag {};
struct uninitializable_int
{
uninitializable_int(uninitialized_tag) {} // No initalization
uninitializable_int(int num) : num(num) {}
int num;
};
demo
- 在这种情况下,java对象是否可以调用本机函数
- 如果 std::vector::clear() 不是静态的,如何在没有实例的情况下调用它?
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- Doxygen - 如何在不生成图形的情况下生成文本调用关系结果
- C++有什么方法可以在既不调用函数模板也不提供其模板参数的情况下引用函数模板?
- C++:带有大括号初始化列表的函数调用表达式 - 标准是否规定在单个元素列表的微不足道的情况下忽略大括号?
- C++ - 如何在不调用其属性的情况下调用类?
- 有没有办法在没有虚拟的情况下使用基类指针调用派生类函数
- 为什么在这种情况下不调用我的虚拟函数实现?
- 在这种情况下,工会成员会调用自己的析构函数吗
- 如何在不迭代的情况下对数组中的每个元素调用方法
- C++-在没有自定义.lib文件的情况下从Lua C模块调用Lua函数
- 如何在派生类中不显式调用base::func()的情况下从基类执行虚拟函数
- C++ 在不释放内存的情况下调用析构函数
- Python在不引用类名的情况下调用类函数
- C++11 - 获取编译时的所有类变量,并在没有 Boost 的情况下为它们调用方法
- C ++:如何在不创建对象的情况下在主函数中调用方法
- 为什么在这种情况下调用非常量右值移动构造函数?
- 在这种情况下,我应该为每个WSASend调用传递唯一的重叠结构吗?
- C++ 在不知道子类型的情况下从父类型调用子方法