Вызов конструктора для повторной инициализации объекта - PullRequest
56 голосов
/ 30 января 2010

возможно ли повторно инициализировать объект класса с помощью его конструктора?

Ответы [ 11 ]

70 голосов
/ 30 января 2010

Вроде. Учитывая класс A:

A a;
...
a = A();   

последнее утверждение не инициализация, это присваивание, но оно, вероятно, делает то, что вы хотите.

47 голосов
/ 30 января 2010

Буквально? Да, с помощью размещения новых. Но сначала вы должны уничтожить ранее построенный объект.

SomeClass object(1, 2, 3);
...
object.~SomeClass(); // destruct
new(&object) SomeClass(4, 5, 6); // reconstruct
...
// Final destruction will be done implicitly

Значение этого не выходит за рамки чисто теоретического. Не делай этого на практике. Все это безобразно за пределами описания.

28 голосов
/ 30 января 2010

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

С этим основным предупреждением, если вы настаиваете на этом, вы можете использовать новое размещение.

// Construct the class
CLASS cl(args);

// And reconstruct it...
new (&cl) CLASS(args);
11 голосов
/ 30 января 2010

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

Редактировать

Я не буду признавать размещение новым, потому что я не хочу, чтобы на работу приходил домашний хищник.

Смотрите этот комикс , но подумайте над темой под рукой ...

10 голосов
/ 30 января 2010

Краткий ответ:

Нет. Если часть предполагаемого поведения вашего объекта должна быть инициализирована несколько раз, то лучший способ реализовать это - доступный метод инициализации. Конструктор вашего класса может просто отложить этот метод.

class C1 {
public:
  C1(int p1, int p2) {
    Init(p1,p2);
  }
  void Init(int p1, int p2) { ... }
};

Угол Нитпикера:

Есть ли какой-то невероятно злой способ вызова конструктора в C ++ после создания объекта? Почти наверняка это C ++ в конце концов. Но это по сути зло, и его поведение почти наверняка не определено стандартом и его следует избегать.

9 голосов
/ 26 мая 2015

В C ++ 11 вы можете сделать это:

#include <type_traits>

template <class T, typename... Args>
void Reconstruct(T& x, Args&&... args)
{
    static_assert(!std::has_virtual_destructor<T>::value, "Unsafe"); 
    x.~T();
    new (&x) T(std::forward<Args>(args)...);
}

Это позволяет использовать Reconstruct, передавая произвольные параметры конструктора любому объекту. Это позволяет избежать необходимости поддерживать кучу Clear методов и ошибок, которые могут легко остаться незамеченными, если в какой-то момент объект изменяется, а метод Clear больше не соответствует конструктору.

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

7 голосов
/ 30 января 2010

Да, вы можете обмануть и использовать новое размещение.
Примечание: я не советую это:

#include <new>

reInitAnA(A& value)
{
    value.~A();            // destroy the old one first.
    new (&value) A();      // Call the constructor 
                           // uses placement new to construct the new object
                           // in the old values location.
}
4 голосов
/ 30 ноября 2016

Я обычно пишу следующее в современном C ++:

SomeClass a;
...
a = decltype(a)();

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

2 голосов
/ 30 января 2010

Вместо того, чтобы разрушать и повторно инициализировать, как предложено некоторыми ответами выше, лучше сделать назначение как ниже. Код ниже является безопасным для исключений.

    T& reinitialize(int x, int y)
    {
        T other(x, y);
        Swap(other); // this can't throw.
        return *this;
    }
2 голосов
/ 30 января 2010

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

Например, вы не написали бы такую ​​игру:

initialize player
code for level 1
...
reinitialize player
code for level 2
...
etc

Вместо этого вы будете стремиться к:

void play_level(level_number, level_data) {
    Player player; //gets "re-initialized" at the beginning of each level using constructor
    //code for level
}

void game() {
    level_number = 1;
    while (some_condition) {
        play_level(level_number, level_data);
        ++level_number;
    }
 }

(Очень грубый план, чтобы передать идею, не предназначенный для удаленной компиляции.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...