C ++中的new运算符分配和初始化内存(通过调用默认构造函数)。如果我想要内存未初始化怎么办?在这种情况下如何分配内存?
在C中,我可以使用malloc,例如只分配内存,而不是初始化它。
答案 0 :(得分:3)
将分配与构建分开是有可能的,但有点棘手。 (Bjarne Stroustrup和我详细讨论了这个问题,大约在1985年。)你要做的是使用:: operator new来获取原始内存。稍后,如果对象类型需要初始化,您可以使用placement-new或其他任何内容进行初始化。这就是STL容器的默认分配器如何分离和构造。
这将获得U类型对象的原始内存:
U *ptr = (U*) ::operator new (sizeof(U));
// allocates memory by calling: operator new (sizeof(U))
// but does not call U's constructor
说到STL ......您可以为:: std :: containers指定自己的分配器。例如,如果使用std :: vector< float>分配浮点数组,它将无偿地将它们初始化为零。 (vector< float>有一个专门化。)你可以改为自己滚动:std :: vector< float,my_own_allocator>。
以下链接中的自定义分配器从默认分配器继承函数以执行几乎所有操作 - 包括原始内存的分配。它会覆盖construct()的默认行为 - 以便什么也不做 - 当实际的构造函数很简单并且不能抛出异常时。
- &GT; Is it possible? std::vector<double> my_vec(sz); which is allocated but not initialized or filled
了解它如何使用展示位置。
::new(static_cast<void*>(ptr)) U;
// Calls class U constructor on ptr.
甚至可以编写您的分配器,以便在编译调试时,它会使用非法数字(NaN&#39; s)填充数组,并在编译发布时保留内存未初始化。我见过的一些最糟糕的错误来自于默认零工作 - 直到他们没有。 DISTRUST DEFAULTS。
还有一个大写字母的东西......避免早期优化。你保存的计算机周期是不是两次初始化对象实际上是值得的吗?
答案 1 :(得分:1)
人们使用两种主要技术来延迟对象的创建。我将展示它们如何应用单个对象,但您可以将这些技术扩展到静态或动态数组。
第一种方法是使用std::aligned_storage
,这是一个美化的char
数组,并考虑了对齐方式:
template<typename T>
class Uninitialized1 {
std::aligned_storage_t<sizeof(T)> _data;
public:
template<typename... Args>
void construct(Args... args) {
new (&_data) T(args...);
std::cout << "Data: " << *reinterpret_cast<T*>(&_data) << "\n";
}
};
请注意,我遗漏了诸如完美转发之类的内容以保持主要观点。
这样做的一个缺点是没有办法让constexpr
构造函数将值复制到类中。这使得它不适合实施std::optional
。
另一种方法使用普通的旧工会。对齐存储,你必须要小心,但对于工会,你必须倍加小心。不要认为我的代码没有错误。
template<typename T>
class Uninitialized2 {
union U {
char dummy;
T data;
U() {}
U(T t) : data(t) {
std::cout << "Constructor data: " << data << "\n";
}
} u;
public:
Uninitialized2() = default;
Uninitialized2(T t) : u(t) {}
template<typename... Args>
void construct(Args... args) {
new (&u.data) T(args...);
std::cout << "Data: " << u.data << "\n";
}
};
一个联盟存储一个强类型的对象,但我们在它之前放置了一个带有简单构造的虚拟对象。这意味着union(以及整个类)的默认构造函数可以变得微不足道。但是,我们也可以选择直接初始化第二个联合成员,即使是以constexpr
兼容的方式。
我遗漏的一件非常重要的事情是你需要手动销毁这些对象。您将需要手动调用析构函数,这应该打扰您,但这是必要的,因为编译器无法保证对象是在第一时间构造的。请帮个忙,研究一下这些技术,以便学习如何正确使用它们,因为有一些非常细微的细节,确保每个物体被正确销毁的事情都会变得棘手。
我(勉强)tested these code snippets有一个小班和司机:
struct C {
int _i;
public:
explicit C(int i) : _i(i) {
std::cout << "Constructing C with " << i << "\n";
}
operator int() const { return _i; }
};
int main() {
Uninitialized1<C> u1;
std::cout << "Made u1\n";
u1.construct(5);
std::cout << "\n";
Uninitialized2<C> u2;
std::cout << "Made u2\n";
u2.construct(6);
std::cout << "\n";
Uninitialized2<C> u3(C(7));
std::cout << "Made u3\n";
}
Clang的输出如下:
Made u1
Constructing C with 5
Data: 5
Made u2
Constructing C with 6
Data: 6
Constructing C with 7
Constructor data: 7
Made u3
答案 2 :(得分:0)
C ++中的new运算符分配和初始化内存(通过调用 默认构造函数)。
恕我直言 - 你误读了新运营商的所作所为。
new运算符分配一个足以容纳对象的内存块。
&#39;新&#39; 不初始化内存。
ctor,如果可用,无需初始化内存,您可以控制它。
如果我不希望内存未初始化怎么办? [SIC]我该怎么做 在那种情况下分配内存?
编译器提供的默认ctor什么都不做。在Foo类中,您可以使用&#34; Foo()= default;&#34;命令编译器提供默认的ctor。
您可以命令编译器禁止默认ctor。对于Foo类,&#34; Foo()=删除;&#34;除非您提供一个,否则将没有默认的ctor。
您可以定义自己的默认ctor,它对内存不起作用。对于您的类,ctor可能没有初始化列表和空体。
注意:有许多嵌入式系统要求ctor被禁止或实现不执行任何操作(这会导致内存设备状态更改)。研究术语&#34;热启动&#34; (软件重置而不影响设备中的数据流。)vs&#34;冷启动&#34; (软件和设备重新启动)vs&#34; power-bounce&#34;。
在某些嵌入式系统中,内存映射的i / o设备未映射到动态内存,操作系统不管理它。在这些情况下,程序员通常会为这些对象提供无操作程序和无操作程序。