Инициализация ref-to-ptr в структуре после malloc()

Я столкнулся с проблемой использования VC++ и Debug CRT с DLL в разработке.

У меня есть такая структура, содержащая некоторые ссылки.

struct DATA
{
    TA*& a;
    TB*& b;
    TC*& c;
    TD*& d;

    char** chars;
    int num_chars;

private:
    // because:
    // DATA a;
    // DATA b;
    // a = b; // is impossible
    DATA& operator=(const DATA&); // append " = delete;" for C++11

    // Default ctor (private because struct should be manually constructed using malloc)
    DATA(TA*& a, TB*& b, TC*& c, TD*& d)
        : a(a),
          b(b),
          c(c),
          d(d),
          chars(NULL),
          num_chars(0)
    {}
};

и построить его так:

DATA*& Get()
{
    static struct DATA *data = (struct DATA*)malloc(sizeof(struct DATA));
    return data;
}

теперь он должен содержать неинициализированные ref-to-ptrs, которые я хочу инициализировать:

void func(TA* a, TB* b, TC* c, TD* d)
{
    Get()->a = a;
    Get()->b = b;
    Get()->c = c;
    Get()->d = d;
    ...
}

который работает для всего, но ref-to-ptrs..

Я получаю INVALID_POINTER_WRITE_FILL_PATTERN_cdcdcdcd на первом Get()->a = a;, когда я делаю !analyze -v -f с помощью WinDbg (в удаленном экземпляре "kd" отладки ядра)

Спасибо за вашу помощь! :)

Изменить: решение

Решение состоит в том, чтобы использовать баллы за правильный ответ.

Сделать c'tor общедоступным необходимо:

struct DATA
{
    TA*& a;
    TB*& b;
    TC*& c;
    TD*& d;

    char** chars;
    int num_chars;

    // Default ctor
    DATA(TA*& a, TB*& b, TC*& c, TD*& d)
        : a(a),
          b(b),
          c(c),
          d(d),
          chars(NULL),
          num_chars(0)
    {}

private:
    // because:
    // DATA a;
    // DATA b;
    // a = b; // is impossible
    DATA& operator=(const DATA&); // append " = delete;" for C++11
};

затем с помощью placement newдля построения структуры:

DATA*& Get(...)
{
    // ... some stuff, overloading, other init-method etc. to init and construct like:
    static struct DATA *data = 
        new(malloc(sizeof(struct DATA))) DATA(...); // At least assign ALL references in the c'tor
    return data;
}

затем используйте его и, возможно, назначьте все, что не является ссылкой:

void func(TA* a, TB* b, TC* c, TD* d)
{
    Get(a, b, c, d);

    Get()->chars = ...
    ...
}

освободить все это нужно явно, вызвав d'tor и free , потому что мы используем placement new:

data->~DATA();
free(data);

person Dominik P    schedule 04.11.2014    source источник
comment
В C у вас нет ссылки; в С++ вы не должны использовать malloc. Выберите ваш язык.   -  person Jarod42    schedule 05.11.2014
comment
просто минус, за что? это вопрос, с которым я столкнулся, есть отрывок, чтобы понять, как это работает, и я указал решение...   -  person Dominik P    schedule 05.11.2014


Ответы (2)


Обратите внимание, что malloc() возвращает неинициализированную память. Объекты C++ должны быть созданы. Способ поместить объект в неинициализированную память — использовать новое размещение (этот код также добавляет очистку):

#include <new>
DATA*& Get()
{
    static DATA *data = new(malloc(sizeof(struct DATA))) DATA(...);
    static std::unique_ptr<DATA, void(*)(DATA*)> clean(data,
                                                [](DATA* d){
                                                    d->~DATA();
                                                    free(data);
                                                });

    return data;
}

В C++ нет способа переустановить ссылки, т. е. их нужно установить во время построения. Лично я бы не стал использовать malloc(), но в любом случае подходящее распределение:

    static DATA* data(new DATA(...));

... или, как указал Jarod42, на самом деле

    static DATA data(...);
    return &data;
person Dietmar Kühl    schedule 04.11.2014
comment
Просто любопытно, как бы вы освободили эту память (ту, что с новым размещением)? - person 0x499602D2; 05.11.2014
comment
или вообще без выделения: static DATA data(..); static DATA* dataptr = &data;. - person Jarod42; 05.11.2014
comment
@ Jarod42: да, есть много способов. Однако, если требуется использование malloc(), его тоже интересно очистить... - person Dietmar Kühl; 05.11.2014
comment
это может быть полезно для очистки при использовании placement new: stackoverflow.com/questions/8918791/ - person Dominik P; 05.11.2014
comment
@DominikP: Я предпочитаю использовать std::unique_ptr<...>(), но да, избавиться от этих парней не совсем просто. - person Dietmar Kühl; 05.11.2014
comment
что произойдет, если malloc вернет NULL в этом коде? - person M.M; 05.11.2014
comment
@MattMcNabb: вы получите неопределенное поведение. Правда, я только что рассмотрел, как построить объект в памяти. Проверить, есть ли это, можно, но это немного сложнее. - person Dietmar Kühl; 05.11.2014
comment
Мой номер 42, а не 43. И он пропускает static вместо unique_ptr. - person Jarod42; 05.11.2014

Вы не можете объявлять ссылки без их инициализации. У вашего struct нет конструктора по умолчанию, так как вы явно объявляете конструктор не по умолчанию. Простого выделения malloc недостаточно для создания действительного объекта DATA, так как это не-POD .

Просто попробуйте объявить настоящий конструктор по умолчанию (т. е. DATA() {}), и вы увидите, что это не сработает из-за ссылочных членов. Если вы хотите использовать malloc для выделения объектов, отличных от POD, вам придется использовать место размещения new.

person misberner    schedule 04.11.2014