请教大家一个 C++内存分配后对象初始化的问题

2021-08-15 11:59:00 +08:00
 GKCY

一直写 Java,最近用到 C++,有个问题不是很明白。 现在有个 allocator 用来分配内存,Key 是个类。 Key* keys = allocator.alloc(sizeof(key) * 10); 我的理解这样 keys 数组是不能直接用的,因为只分配了内存,类没有初始化。 但是这该怎么初始化呢?我能想到的笨方法是,先分配一个 buf 内存,然后循环里通过 new (buf)一个个初始化,但是这样感觉代码很丑陋,之前没接触过 C++,问题可能很蠢,请大佬不要介意。

2235 次点击
所在节点    C++
19 条回复
afx
2021-08-15 12:46:29 +08:00
分配内存一定得需要 allocator 吗,使用 key * pKeys = new keys[10], new 操作会自动调用构造函数,在构造函数里写初始化代码就好了。
ashong
2021-08-15 12:51:00 +08:00
直接 new,类初始化由类构造函数完成
SJ2050cn
2021-08-15 12:55:11 +08:00
用 allocator 的话他有个 construct 方法可以用于初始化,给你一段随手写的代码感受一下:

```c++
#include <memory>
#include <iostream>
#include <string>

class Key {
public:
explicit Key(int i):k(i) {}

void print_k() { std::cout << "k = " << k << std::endl; }
private:
int k;
};

int main() {
std::allocator<Key> alloc;

Key* key_arr = alloc.allocate(10);

for (int i = 0; i < 10; i++) {
alloc.construct(&key_arr[i], i);
key_arr[i].print_k();
}

alloc.deallocate(key_arr, 10);
return 0;
}
```
xylxAdai
2021-08-15 13:01:13 +08:00
你都用 c++了,为啥不能直接用 new 呢。或者直接 alloc 之后 placement new 也是一样,大致用法和你这个用法类似,STL 就是这么做的,一个一个的对那块内存执行显示构造,你把它封装好其实也不怎么丑陋。https://xinyoulinxi.github.io/2017/07/27/new-and-delete/,我之前博客也写过 new 的细节。可以看看
choury
2021-08-15 13:01:42 +08:00
两种方式,一种重写 operator new[] 操作符,里面 allocate 内存,然后循环调用 new
另外一种,直接把分配的内存指针当作第二个参数传给 new[],比如 new(keys) key[10]
xylxAdai
2021-08-15 13:01:54 +08:00
xylxAdai
2021-08-15 13:04:00 +08:00
@xylxAdai 不过这样最大的问题就是析构也需要手动 operate delete 和执行析构罢了,挺麻烦的,没啥必要。
GeruzoniAnsasu
2021-08-15 15:15:12 +08:00
巧。前几天我一个群里刚好有人问这几行代码什么意思,你可以参考一下这个 new 的写法

learningman
2021-08-15 15:17:38 +08:00
@xylxAdai #6 你加个空格就不会把后面的带上了
GKCY
2021-08-15 17:18:05 +08:00
回复一楼,内存分配必须用这个项目里的 allocator,不能直接 new, 这个项目偏 C 语言。另外,非常感谢大家提供的思路,对我帮助很大。
codehz
2021-08-15 19:29:40 +08:00
placement new 就是楼上展示的那种奇怪的 new 语法
作用就是在指定位置调用构造函数,可以说是完全符合需求了
new (一个能拿到地址的表达式) 类型{构造函数参数}
(打包一下实现成标准 allocator 就可以在 stl 容器里使用了)
netcan
2021-08-15 20:32:59 +08:00
@GeruzoniAnsasu 没有 forward 和万能引用
levelworm
2021-08-15 21:21:12 +08:00
@GKCY 看来是自己写的内存分配器了?
openmm
2021-08-15 21:42:48 +08:00
c 语言的话用 malloc/free 不行吗 初始化用 memset
GeruzoniAnsasu
2021-08-16 00:28:29 +08:00
@netcan 也不能构造对象数组呢

其实我原本想表达的是 allocator+placement new 还挺常见的
LifStge
2021-08-16 00:58:24 +08:00
要分清 是要 c++的解法 还是 c 的解法 截然不同的
用 c 就是申请 自己挨个初始化 挨个挨个析构释放 再 释放内存
用 c++ 同样是这个流程 只是加入语法特性 自动化处理这些 最终的流程都是一样的 方法很多
自己做通用封装 , 重载 new , 使用标准库的容器 而不是原始数组, 如果要求使用指定的内存分配器 用容器的话 就自定义内存分配器 .

当然如果只是项目的整体需要用 c++来编译的话 其他自己随便搞 那还关心什么 c++惯用法呢
GKCY
2021-08-17 13:16:33 +08:00
@levelworm 是的
secondwtq
2021-08-17 23:39:11 +08:00
是,你得先分配内存,然后再调用构造函数,才算初始化完成。(参考“把大象放进冰箱里”的流程)

我认为你这个代码的问题:
> Key* keys = allocator.alloc(sizeof(key) * 10);

在于,allocator 只知道分配的内存块的大小,别的什么信息都不知道,所以虽然是个 C++ 的函数调用(当然也可能是个 C 函数指针),但是没法调用相应的构造函数。
要想方便地完成整个分配过程,就得让函数获得你分配的对象的类型信息,然后用这个类型 sizeof 就能得到大小信息。C++ 里面传递类型信息是使用 template 。就是像 #8 那样写一个 wrapper (当然 #8 的代码有一点问题)。

你也可以直接重载 new 和 delete operator,这样编译器会自动帮你生成调 ctor 的代码,什么 std::make_unique 之类的标准库功能默认也会用你的 allocator 。对于“偏 C 语言”的项目应该是比较简单的方法。或者把你这个 allocator 对象包装成一个符合 C++ 标准 Allocator 的对象,也可以达到相同效果。不过这样就不够“C with Classes”了~

Anyway,思路都是一样的。对于“丑陋”的代码,通用的手段就是抽象和封装,最常用也最通用的工具就是函数。C++ 相对于 C 的最大好处就是提供了更强的抽象能力,在这个问题里就是你可以通过一个函数来封装你觉得很“丑陋”的初始化循环。
hxndg
2021-08-19 23:44:25 +08:00
如果是 C 的话,你就直接上 malloc 吧,然后初始化吧
而且你都指针了,必然是要自己管理内存了。

如果 C++的话还是尽量避免用原始的指针吧。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/795873

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX