2016-10-07 8 views
-1

В некоторых встроенных ситуациях память должна перемещаться с помощью функций стиля memcopy (например, из внешней памяти или с помощью закрытых вызовов API).Конструкция объекта «memcopy» объекта в C++

Когда такое C++ объект должен быть перемещен таким образом, однако он не имеет конструктор по умолчанию, вы не можете сделать что-то вроде этого,:

class Object { 
    //local data 
public: 
    Object(/* not a default constructor */){} 
} 

//elsewhere: 
Object o; //compiler will complain here... 
memcpy_like_function(src_address, &o, sizeof(o)); 

потому что Object не имеет конструктор по умолчанию, и, таким образом, компилятор будет жаловаться на создание Object o.

Некоторые заметки из вещей, которые показали в комментариях:

  • memcpy_like_function является как тетсра, это на самом деле не тетсра. src_address не является указателем на адрес, который я могу достигнуть, или int, представляющий указатель на адрес, который я могу достигнуть. Это int, представляющий адрес в пространстве памяти, к которому я не могу добраться. Единственная возможность получить доступ к этому пространству памяти - это эта функция.
  • Object не имеет конструктора по умолчанию, не имеет виртуальных функций, не наследуется и не наследует ничего. Object - trivially copyable.

Каков правильный способ справиться с созданием такого объекта в этой ситуации, не помещая его в кучу? Предпочтительно, я хотел бы получить объект, выделенный стек, который будет корректно вести себя с областью и деструкторами. Для целей этого вопроса Object не наследует ни от чего.

+0

Прошу прокомментировать до голосования и голосования, чтобы закрыть .... это невероятно грубо, чтобы поступать иначе. –

+0

@ Eichhörnchen: поскольку ячейка памяти, в которой она находится, недоступна за пределами закрытого вызова API ... –

+0

@AndrewSpott Закрытие голосования будет включать в себя причину - иногда дополнительный комментарий не требуется –

ответ

2

Вы не можете безопасно скопировать объект, используя функцию memcpy, если объект не является типом POD. Если объект представляет собой тип POD, вы должны быть в состоянии использовать:

char destination[sizeof(Object)]; 
memcpy_like_function(src_address, destination, sizeof(destination)); 
Object* ptr = reinterpret_cast<Object*>(destination); 

Моя кишка чувство говорит, что это должно работать при любых компиляторов для типов POD. Является ли это причиной неопределенного поведения по некоторым правилам стандарта, я не уверен.

Если это является причиной неопределенного поведения в соответствии с некоторыми стандартами стандарта, вы не сможете сохранить POD-тип в двоичном файле и прочитать его из двоичного файла стандартным образом. Они полагаются на бит-шаблон, записанный в файл, и считывают из файла для представления объекта.

Следующая программа производит ожидаемый результат под g ++.

#include <iostream> 
#include <cstring> 

struct Object 
{ 
    int i; 
    double d; 
    Object(int ii, double dd) : i(ii), d(dd) {} 
}; 

int main() 
{ 
    Object o1(10, 20.34); 
    char dest[sizeof(Object)]; 
    memcpy(dest, &o1, sizeof(dest)); 
    Object* ptr = reinterpret_cast<Object*>(dest); 
    std::cout << o1.i << ", " << o1.d << std::endl; 
    std::cout << ptr->i << ", " << ptr->d << std::endl; 
} 

Update, в ответ на замечания OP в

Следующая программа работает, как ожидалось при г ++.

#include <iostream> 
#include <cstring> 

struct Object 
{ 
    int i; 
    double d; 
    Object(int ii, double dd) : i(ii), d(dd) {} 
}; 

Object testFunction(Object o1) 
{ 
    char dest[sizeof(Object)]; 
    memcpy(dest, &o1, sizeof(dest)); 
    Object* ptr = reinterpret_cast<Object*>(dest); 
    return *ptr; 
} 

int main() 
{ 
    Object o1(10, 20.34); 
    Object o2 = testFunction(o1); 
    std::cout << o1.i << ", " << o1.d << std::endl; 
    std::cout << o2.i << ", " << o2.d << std::endl; 
    o2.i = 25; 
    o2.d = 39.65; 
    std::cout << o2.i << ", " << o2.d << std::endl; 
} 
+0

Обратите внимание, что не будет разрешено использовать значение «Object» lvalue для последующего доступа к получателю –

+0

Это не то же самое. Это дает мне массив стека с теми же данными, но не стек. –

+0

Это все еще объект, который я не могу вернуть из функции ... хотя редактирование вопроса, чтобы сделать это ясно, придется ждать. –

2

потому что Object не имеет конструктор по умолчанию

Когда Object не имеет конструктор по умолчанию,

//Object o;//NG 

нет никакого способа построить Object, если только вызов другая функция ctor или factory. Из-за этого вы не можете вызвать функцию memcpy.

Если у вас есть способ построения Object, чтобы использовать тетср подобных функции, Object класса сусла грантополучателей, что trivially copyable class и standard-layout class (не равно POD class).

trivial class: trivially copyable class & & не имеет по умолчанию определенный пользователем конструктор не
POD class: trivial class & & standard-layout class

4

Это кажется ужасно плохая идея, но при условии, что ваш memcpy_like_function на самом деле работает, то вы можете просто добавить конструктор до Object

class Object { 
    //local data 
public: 
    Object(void* src_address) 
    { 
     memcpy_like_function(src_address, this, sizeof(*this)); 
    } 
}; 

//elsewhere: 
Object o(src_address); 
1

Что вы можете сделать, просто использовать массив байтов, а затем бросить.

Примечание: то, что вы делаете, на самом деле не является хорошей практикой на C++, как правило, вам следует использовать операторы присваивания или конструкторы копирования, и вы должны придерживаться более безопасных отливок C++, а не просто сбрасывать C-стиль грубой силы.

Во всяком случае, как говорится, этот код будет работать:

class Object { 
    int i; 

public: 
    Object(int i) : i(i) {} 

    void foo() const { 
     std::cout << i << std::endl; 
    } 
}; 

void bla() { 
    Object one(1); 
    char *bytes = new char[sizeof(Object)]; 
    memcpy(bytes, &one, sizeof(Object)); 
    Object &anotherOne = (Object &) *bytes; 
    anotherOne.foo(); 
    const Object &oneMore = (Object) *bytes; 
    oneMore.foo(); 
    Object *oneMoreTime = (Object *) bytes; 
    oneMoreTime->foo(); 
    delete[] bytes; 
} 

Выход:

1 
1 
1 

В заключение, необходимо выделить область памяти в стеке или в куче который станет экземпляром объекта.

+0

Примечание: это неопределенное поведение. (Может или может не работать для какой-либо конкретной реализации) –

+0

'(Object) * bytes' использует первое значение' char' для вызова конструктора 'Object', это, вероятно, не то, что было предназначено –

+0

Не совсем: C указывает, что структуры распределены смежно в памяти, и поэтому в C++, если поля данных POD, как в этом случае, тогда это будет работать. Однако в тех случаях, когда объекты не являются POD, вы правы, это может не сработать. –