一直写 Java,最近用到 C++,有个问题不是很明白。
现在有个 allocator
用来分配内存,Key
是个类。
Key* keys = allocator.alloc(sizeof(key) * 10);
我的理解这样 keys 数组是不能直接用的,因为只分配了内存,类没有初始化。
但是这该怎么初始化呢?我能想到的笨方法是,先分配一个 buf
内存,然后循环里通过 new (buf)
一个个初始化,但是这样感觉代码很丑陋,之前没接触过 C++,问题可能很蠢,请大佬不要介意。
1
afx 2021-08-15 12:46:29 +08:00 1
分配内存一定得需要 allocator 吗,使用 key * pKeys = new keys[10], new 操作会自动调用构造函数,在构造函数里写初始化代码就好了。
|
2
ashong 2021-08-15 12:51:00 +08:00 via iPhone
直接 new,类初始化由类构造函数完成
|
3
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; } ``` |
4
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 的细节。可以看看
|
5
choury 2021-08-15 13:01:42 +08:00
两种方式,一种重写 operator new[] 操作符,里面 allocate 内存,然后循环调用 new
另外一种,直接把分配的内存指针当作第二个参数传给 new[],比如 new(keys) key[10] |
6
xylxAdai 2021-08-15 13:01:54 +08:00
|
7
xylxAdai 2021-08-15 13:04:00 +08:00
@xylxAdai 不过这样最大的问题就是析构也需要手动 operate delete 和执行析构罢了,挺麻烦的,没啥必要。
|
8
GeruzoniAnsasu 2021-08-15 15:15:12 +08:00
|
9
learningman 2021-08-15 15:17:38 +08:00
@xylxAdai #6 你加个空格就不会把后面的带上了
|
10
GKCY OP 回复一楼,内存分配必须用这个项目里的 allocator,不能直接 new, 这个项目偏 C 语言。另外,非常感谢大家提供的思路,对我帮助很大。
|
11
codehz 2021-08-15 19:29:40 +08:00 via Android
placement new 就是楼上展示的那种奇怪的 new 语法
作用就是在指定位置调用构造函数,可以说是完全符合需求了 new (一个能拿到地址的表达式) 类型{构造函数参数} (打包一下实现成标准 allocator 就可以在 stl 容器里使用了) |
12
netcan 2021-08-15 20:32:59 +08:00
@GeruzoniAnsasu 没有 forward 和万能引用
|
14
openmm 2021-08-15 21:42:48 +08:00
c 语言的话用 malloc/free 不行吗 初始化用 memset
|
15
GeruzoniAnsasu 2021-08-16 00:28:29 +08:00
|
16
LifStge 2021-08-16 00:58:24 +08:00
要分清 是要 c++的解法 还是 c 的解法 截然不同的
用 c 就是申请 自己挨个初始化 挨个挨个析构释放 再 释放内存 用 c++ 同样是这个流程 只是加入语法特性 自动化处理这些 最终的流程都是一样的 方法很多 自己做通用封装 , 重载 new , 使用标准库的容器 而不是原始数组, 如果要求使用指定的内存分配器 用容器的话 就自定义内存分配器 . 当然如果只是项目的整体需要用 c++来编译的话 其他自己随便搞 那还关心什么 c++惯用法呢 |
18
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 的最大好处就是提供了更强的抽象能力,在这个问题里就是你可以通过一个函数来封装你觉得很“丑陋”的初始化循环。 |
19
hxndg 2021-08-19 23:44:25 +08:00
如果是 C 的话,你就直接上 malloc 吧,然后初始化吧
而且你都指针了,必然是要自己管理内存了。 如果 C++的话还是尽量避免用原始的指针吧。 |