Определение объекта без вызова его конструктора в С++
В С++ я хочу определить объект как член класса, подобного этому:
Object myObject;
Однако выполнение этого будет пытаться назвать его беззазорным конструктором, которого не существует. Однако мне нужно, чтобы конструктор вызывался после того, как содержащий класс сделал некоторую инициализацию. Что-то вроде этого.
class Program
{
public:
Object myObject; //Should not try to call the constructor or do any initializing
Program()
{
...
//Now call the constructor
myObject = Object(...);
}
}
Ответы
Ответ 1
Сохраните указатель на Object
, а не фактический Object
следующим образом:
class Program
{
public:
Object* myObject; // Will not try to call the constructor or do any initializing
Program()
{
//Do initialization
myObject = new Object(...); // Initialised now
}
}
Не забывайте delete
его в деструкторе. Современный С++ помогает вам там, поскольку вы можете использовать auto_ptr shared_ptr, а не указатель необработанной памяти.
Ответ 2
Другие опубликовали решения с использованием raw-указателей, но умная указатель была бы лучшей идеей:
class MyClass {
std::unique_ptr<Object> pObj;
// use boost::scoped_ptr for older compilers; std::unique_ptr is a C++0x feature
public:
MyClass() {
// ...
pObj.reset(new Object(...));
pObj->foo();
}
// Don't need a destructor
};
Это позволяет избежать необходимости добавления деструктора и неявно запрещает копирование (если вы не написали свои собственные operator=
и MyClass(const MyClass &)
.
Если вы хотите избежать выделения отдельной кучи, это можно сделать с помощью boost aligned_storage
и размещения new. Непроверенные:
template<typename T>
class DelayedAlloc : boost::noncopyable {
boost::aligned_storage<sizeof(T)> storage;
bool valid;
public:
T &get() { assert(valid); return *(T *)storage.address(); }
const T &get() const { assert(valid); return *(const T *)storage.address(); }
DelayedAlloc() { valid = false; }
// Note: Variadic templates require C++0x support
template<typename Args...>
void construct(Args&&... args)
{
assert(!valid);
new(storage.address()) T(std::forward<Args>(args)...);
valid = true;
}
void destruct() {
assert(valid);
valid = false;
get().~T();
}
~DelayedAlloc() { if (valid) destruct(); }
};
class MyClass {
DelayedAlloc<Object> obj;
public:
MyClass() {
// ...
obj.construct(...);
obj.get().foo();
}
}
Или, если Object
является скопируемым (или подвижным), вы можете использовать boost::optional
:
class MyClass {
boost::optional<Object> obj;
public:
MyClass() {
// ...
obj = Object(...);
obj->foo();
}
};
Ответ 3
Если у вас есть доступ к boost, есть удобный объект, который предоставляется с именем boost::optional<>
- это позволяет избежать необходимости динамического выделения, например
class foo
{
foo() // default std::string ctor is not called..
{
bar = boost::in_place<std::string>("foo"); // using in place construction (avoid temporary)
}
private:
boost::optional<std::string> bar;
};
Ответ 4
Вы также можете переписать свой код, чтобы использовать список инициализаторов конструктора, если вы можете переместить другую инициализацию в конструкторы:
class MyClass
{
MyObject myObject; // MyObject doesn't have a default constructor
public:
MyClass()
: /* Make sure that any other initialization needed goes before myObject in other initializers*/
, myObject(/*non-default parameters go here*/)
{
...
}
};
Вам нужно знать, что после такого шаблона вы попадаете в путь, где вы выполняете большую работу в конструкторах, что, в свою очередь, приводит к необходимости обработки исключений и безопасности (как канонический способ возврата ошибки из конструктора - это исключение).
Ответ 5
Для этого вы можете использовать указатель (или интеллектуальный указатель). Если вы не используете интеллектуальный указатель, убедитесь, что ваша свободная память кода при удалении объекта. Если вы используете смарт-указатель, не беспокойтесь об этом.
class Program
{
public:
Object * myObject;
Program():
myObject(new Object())
{
}
~Program()
{
delete myObject;
}
// WARNING: Create copy constructor and = operator to obey rule of three.
}