Почему я не могу вызвать конструктор моего класса из экземпляра этого класса на C++?

StackOverflow https://stackoverflow.com/questions/2146901

  •  23-09-2019
  •  | 
  •  

Вопрос

Когда объект класса может вызвать деструктор этого класса, как если бы это была обычная функция?Почему он не может вызвать конструктор того же класса, что и одну из своих обычных функций?Почему компилятор мешает нам это сделать?

Например:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 objC.c() ;  // compilation error
}
Это было полезно?

Решение

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

Однако в некоторых случаях вам может потребоваться точный контроль над управлением памятью и возможность явного создания и уничтожения объектов в памяти, которыми вы управляете.Для этой цели в языке предусмотрено «размещение нового» для создания объекта в произвольном месте и явные вызовы деструктора для уничтожения объектов, созданных таким образом.Явный вызов конструктора не будет полезен, поскольку вам нужно иметь возможность указать местоположение нового объекта, поэтому вместо этого вы получите «размещение нового».Явного вызова деструктора достаточно, поэтому нет необходимости изобретать какое-то соответствующее «удаление размещения».

Так:явные вызовы конструктора недопустимы, поэтому они не разрешены.Существует допустимое использование явных вызовов деструктора, поэтому они разрешены (синтаксически) с правилом, согласно которому вы должны использовать их только для объектов, которые иначе не будут уничтожены, т.е.объекты, созданные с использованием «размещения нового», и в этом случае вызовите их ровно один раз.Их использование любым другим способом, как и многие другие ошибки C++, скомпилируется, но приведет к неопределенному поведению.

Другие советы

Я думаю, вы можете явно вызвать деструктор, если убедитесь, что экземпляр заменен/воссоздан с помощью вызова нового размещения:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

int main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles
 new (&objC) c;  // placement new invokes constructor for the given memory region
}

Я никогда не видел этого на практике, но логически это должно работать (если только конструктор c не может выдать исключение, и в этом случае, я полагаю, ад может вырваться на свободу во время раскручивания стека).

Однако то, что вам, вероятно, нужно, это просто назначение:

objC = c();

Если у деструктора есть интересующие вас побочные эффекты, реализуйте присваивание, используя идиому копирования и замены, которая вызывает вызов деструктора для «левого» значения.

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

Это похоже на работу по созданию и удалению:

int * p = new int;    // call to new needs no existing instance
delete p;             // call to delete requires existing instance

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

Если вам действительно нужно сделать что-то подобное, просто создайте дополнительную функцию и вызовите ее извне И из самого конструктора, но давайте посмотрим, что произойдет, когда вам действительно понадобится такой вызов:

#include<new>

class A
{
//members
};

int main()
{
//allocate buffer
char* buffer = new char[sizeof(A)];
//construct A on that memory space
A * ptrToA = ::new (buffer) A();
//destroy the object
ptrToA->~A();
//deallocate the buffer
delete[] buffer;
}

Одним из экземпляров, где вы можете найти новое использование размещения, является стандартные контейнеры.Ассистент берет на себя ответственность за распределение буфера (выделить член), а объекты построены над этим буфером, поскольку они добавляются в контейнер.Например, когда вы делаете резерв на векторном объекте, он резервирует пространство для N объектов значения выделяет пространство для N объектов, но не конструирует их.Затем, когда вы делаете push_back и т.д., чтобы добавить элементы, они создаются над этим буфер.В основном, это метод, чтобы уменьшить накладные расходы повторные вызовы функции выделения памяти.А затем, когда это сделано, векторный деструктор уничтожит объекты, вызывающие деструктор явно для всех объектов в нем, а затем вызовите deallocate() функция распределителя для того чтобы выпустить память.Надеюсь это поможет.

Попробуйте это:

obj.ClassName::ClassName() ;// это работает в компиляторе VC 6.0

Другой способ подумать об ограничении состоит в том, что конструктор нет просто еще одна функция.Рассмотрим его определение:в отличие от других функций, она не имеет возвращаемого значения и может иметь список инициализаторов.То, что в нем есть большая часть синтаксиса функции, является своего рода совпадением;на самом деле он существует только для инициализации нового экземпляра объекта.

Конструктор "c()" используется с новым, т.е.

c objC = new c();

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

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

c objC;
objC.~c();  // force objC's destructor to run
new(&objC); // force objC's constructor to run

Почему разработчики языка не использовали вместо этого этот синтаксис?

c objC;
delete(&objC) c; // force objC's destructor to run
new(&objC) c;    // force objC's constructor to run

Или почему бы и нет:

c objC
objC.~c(); // force objC's destructor to run
objC.c();  // force objC's constructor to run

Мое мнение о том, почему они выбрали такой синтаксис:Первый вариант более гибок, чем второй.Я могу вызвать конструктор/деструктор по любому адресу, а не только по экземпляру.Такая гибкость необходима для распределения, а не для уничтожения.Кроме того, удаление первого варианта кажется очень опасным, если в дело вступают виртуальные деструкторы.

вы можете вызвать конструктор класса экземпляра, используя typeof:

class c
{
public:
   void add() ;
   c();
   ~c() ;
};

void main()
{
 c objC  ;
 objC.add() ;
 objC.~c() ; // this line compiles (but is a bad idea)
 typeof objC otherObjC;  // so does this.
}

Это не влияет на значение экземпляра objC, но создает новый экземпляр otherObjC с использованием objCконструктор класса.

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

Кто сказал, что ты не можешь?Вам просто нужно знать, как это сделать.

void Foo::Bar() {
  *this = Foo(); // Reset *this
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top