Я играл с шаблонами, когда был удивлен, что приведенный ниже код не работает должным образом:
#include <iostream>
#include <string>
#include <cstring>
template <class Object>
class CreatorTWO {
public:
CreatorTWO (void) {}
~CreatorTWO (void) throw () {}
template <typename... Object_Params>
Object* create (const Object_Params&... params_) {
Object _temp_obj = Object(params_...);
size_t _obj_bytes = sizeof (_temp_obj);
void * _mem_block = std::malloc (_obj_bytes);
std::memmove(_mem_block,&_temp_obj,_obj_bytes);
//The line below prints the dereferenced object before it is returned:
std::cout << *(static_cast <Object*> (_mem_block)) << '\n';
return static_cast <Object*> (_mem_block);
}
};
int main (int argc, char* argv[]) {
CreatorTWO <std::string> _c2;
std::string* _strp = _c2.create("Hello");
std::cout << *_strp << '\n';
std::free (_strp);
return 0;
}
Приведенный выше код должен создать объект с переменным числом параметров, передаваемых ему. Однако, когда я создал экземпляр с шаблоном std :: string, передача аргумента «Hello» должна дать мне указатель на строку, содержащую «Hello». Однако, это не так. Если вы запустите приведенный выше код, значения pre и post будут разными, а pre будет правильным. Кто-нибудь знает, что вызывает это нежелательное поведение? Благодарю.
C ++ находится в неудобном месте между необработанным доступом к базовому оборудованию и языком высокого уровня, который способен к быстрой разработке.
Общий принцип заключается в том, что вы не можете использовать memcpy для перемещения объекта, который вы в этом случае сломали.
Когда вы создаете класс.
class Example {
protected:
char * ptr;
size_t len;
public:
Example( const char * str ) {
len = strlen( str );
ptr = new char[ len + 1];
}
virtual ~Example() {
delete [] ptr;
}
Example & operator=( const Example & rhs ) {
if( &rhs != this ) {
delete [] ptr;
len = rhs.len();
ptr = new char[ len + 1 ];
strcpy( ptr, rhs.ptr );
}
}
Example( const Example & src ) {
len = src.len;
ptr = new char[ len + 1];
strcpy( ptr, src.ptr );
}
Example() {
ptr = new char[1];
ptr[0] = '\0';
len = 0;
}
};
Идея доступа к внутреннему состоянию не позволяет разработчику класса дать гарантии правильности.
Если бы класс Example был memcpy’ed, то было бы два экземпляра класса, причем у обоих было установлено значение ptr. Если один из них уничтожен, он освобождает память, адресуемую ptr в другом, нарушая внутреннее состояние других классов.
Когда класс хорошо реализован, копирование с использованием оператора = позволяет разработчику понять, что может быть передано и что необходимо скопировать, и может обеспечить правильное поведение.
По этой причине изменение или копирование класса с использованием необработанных операторов памяти ведет к неопределенному поведению.
Других решений пока нет …