V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GKCY
V2EX  ›  C++

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

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

    一直写 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   ·   我们的愿景   ·   实用小工具   ·   1602 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 17:00 · PVG 01:00 · LAX 10:00 · JFK 13:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.