请教大家一个 C++内存分配后对象初始化的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GKCY
V2EX    C++

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

  •  
  •   GKCY 2021-08-15 11:59:00 +08:00 2891 次点击
    这是一个创建于 1585 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    19 条回复    2021-08-19 23:44:25 +08:00
    afx
        1
    afx  
       2021-08-15 12:46:29 +08:00   1
    分配内存一定得需要 allocator 吗,使用 key * pKeys = new keys[10], new 操作会自动调用构造函数,在构造函数里写初始化代码就好了。
    ashong
        2
    ashong  
       2021-08-15 12:51:00 +08:00 via iPhone
    直接 new,类初始化由类构造函数完成
    SJ2050cn
        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;
    }
    ```
    xylxAdai
        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 的细节。可以看看
    choury
        5
    choury  
       2021-08-15 13:01:42 +08:00
    两种方式,一种重写 operator new[] 操作符,里面 allocate 内存,然后循环调用 new
    另外一种,直接把分配的内存指针当作第二个参数传给 new[],比如 new(keys) key[10]
    xylxAdai
        7
    xylxAdai  
       2021-08-15 13:04:00 +08:00
    @xylxAdai 不过这样最大的问题就是析构也需要手动 operate delete 和执行析构罢了,挺麻烦的,没啥必要。
    GeruzoniAnsasu
        8
    GeruzoniAnsasu  
       2021-08-15 15:15:12 +08:00
    巧。前几天我一个群里刚好有人问这几行代码什么意思,你可以参考一下这个 new 的写法

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

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

    当然如果只是项目的整体需要用 c++来编译的话 其他自己随便搞 那还关心什么 c++惯用法呢
    GKCY
        17
    GKCY  
    OP
       2021-08-17 13:16:33 +08:00 via Android   1
    @levelworm 是的
    secondwtq
        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 的最大好处就是提供了更强的抽象能力,在这个问题里就是你可以通过一个函数来封装你觉得很“丑陋”的初始化循环。
    hxndg
        19
    hxndg  
       2021-08-19 23:44:25 +08:00
    如果是 C 的话,你就直接上 malloc 吧,然后初始化吧
    而且你都指针了,必然是要自己管理内存了。

    如果 C++的话还是尽量避免用原始的指针吧。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3039 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 12:22 PVG 20:22 LAX 04:22 JFK 07:22
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86