Конструктор вызвал уже созданный объект - PullRequest
6 голосов
/ 18 февраля 2011

Если я вызову конструктор для уже построенного объекта или структуры, будет ли он выделять новое пространство или просто использовать существующее пространство? Итак, является ли выделение первого объекта более ресурсоемким? Как это:

struct F {
    int a,b,c,d;
    F(int _a, int _b) {a = _a; b = _b}; 
    void a(int _a, int _b) {a = _a; b = _b};
};  

//first constructor call
F f = F(5, 6);

//second constructor call on an already constructed object
f = F(7, 8);

//third constructor call on an already constructed object
f(7, 8);

//is the constructor call more res. intesive, than the call to a function which does the same? 
f.a(9, 0)

Является ли вызов конструктора более ресурсоемким, чем вызов функции, которая делает то же самое (void a(...))?

Деструктор вызывается, когда я вызываю конструктор для уже созданного объекта?

Ответы [ 5 ]

7 голосов
/ 18 февраля 2011

Во-первых, тег [c] неуместен, поскольку конструкторы доступны только для C ++. Я предполагаю, что предоставленный вами фрагмент кода на самом деле - C ++, а не какой-то странный диалект языка C. C ++ и C - это разные языки ; не помечайте свои вопросы как оба, так как вы получите разные ответы для каждого.

Во-вторых, ваше определение конструктора неверно. Конструкторы должны иметь то же имя, что и сам класс. Так что f() должно было быть F(). Да, чувствительность к регистру имеет значение в C ++! Я предполагаю, что это то, что вы имели в виду для остального фрагмента кода. OP просто сделал опечатку.

Прежде чем объяснить, что делает остальная часть вашего кода, вы должны понимать, что все классы (и структуры) в C ++ имеют специальные функции-члены , которые автоматически генерируются компилятором, если вы их не предоставляете , То есть ваш фрагмент кода в основном такой же, как:

struct F
{
    F(int _a, int _b) {a = _a; b = _b};  // constructor
    ~F() {}                              // destructor
    F(const F& rhs)                      // copy constructor
        : a(rhs.a)
        , b(rhs.b)
        , c(rhs.c)
        , d(rhs.d)
    {}
    F& operator=(const F& a)             // copy assignment operator
    {
        a = rhs.a;
        b = rhs.b;
        c = rhs.c;
        d = rhs.d;
        return *this;
    }

    void a(int _a, int _b) {a = _a; b = _b};   // one of your functions

    int a;
    int b;
    int c;
    int d;
};

Если вы не определили конструктор копирования, оператор назначения копирования или деструктор, компилятор сгенерирует их для вас. Кроме того, если вы не предоставите какой-либо другой конструктор, компилятор сгенерирует конструктор по умолчанию. Для класса F не существует конструктора по умолчанию, поскольку уже существует конструктор (не для копирования), который принимает два аргумента.

Реализация по умолчанию конструктора копирования и оператора назначения копирования просто копирует каждый элемент данных.

Причина, по которой существуют специальные функции-члены, заключается в том, что C ++ обобщает понятие копирования примитивных типов в определенные пользователем объекты. Учтите это:

int a = 42;
int b = 13;
b = a;

С такими примитивными типами, как int s, вы можете просто копировать его значение. C ++ обобщает семантику копирования для объектов, так что вы можете сделать это:

F f(10, 20); // calls first constructor
F g(30, 40); // calls first constructor
g = f;       // calls g's copy-assignment operator.

Теперь вы можете увидеть, как это относится к вашему коду:

F f = F(5,6); 

Строка выше создает временный объект F, а затем копирует временный объект в f через конструктор копирования. Временный объект F затем уничтожается.

f = F(7,8);

В приведенной выше строке создается другой временный объект F, а затем назначается временный объект в f с помощью оператора копирования. Временный объект F затем уничтожается. Исходный f объект не разрушен.

f.a(9,0)   

Строка выше - это обычный вызов функции для объекта с именем f.

Для вашего фрагмента кода, если предположить, что компиляторы не оптимизируют временные значения (они обычно это делают), то вызов функции a является "менее ресурсоемким", поскольку в этом случае временные действия не производятся. Однако для вашего первого вызова конструктора вы можете просто сделать это:

F f(5,6); // Constructor called; no temporaries are made

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

Как я много раз рекомендовал, пожалуйста, возьмите хорошую книгу по C ++ и прочитайте ее. Специальные функции-члены и то, что они делают, являются фундаментальными для C ++.

3 голосов
/ 18 февраля 2011

(В коде, который вы разместили, вы использовали строчную букву f вместо прописной буквы F для конструктора, что, как я полагаю, является опечаткой)

Ваш вопрос интересен, поскольку выспросил, и код, который вы написали, не совпадают.В коде вы написали

f = F(7, 8);

Это вызывает конструктор F, но не для существующего объекта f.Вместо этого это создает временный объект F, инициализированный вызовом конструктора tue с аргументами 7 и 8, а затем использует оператор присваивания, чтобы установить существующую переменную f равной этому новому объекту.Следовательно, конструктор не вызывается дважды на объекте;вместо этого здесь вызывается задание.В более общем случае, если вы когда-либо назначите существующему объекту новое значение, для копирования будет использован оператор присваивания, а не конструктор.Это будет повторно использовать существующую память для объекта, хотя это может инициировать выделение и освобождение вспомогательной памяти.

Не существует безопасного способа дважды вызвать конструктор объекта.Если вы действительно хотите вызвать конструктор объекта, который уже был создан, вы можете сделать это с помощью размещения new:

F f(3, 5);
new (&f) F(7, 9);

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

2 голосов
/ 18 февраля 2011

Я игнорирую реализацию в OP и иду прямо к QA:

если я вызову конструктор для уже построенного объекта / структуры, будет ли он выделять новое пространство

нет, объект не будет перераспределен. будет вызвана реализация конструктора (например, вызов функции).

Значит, выделение первого объекта более ресурсоемко?

как правило, вы не должны этого делать, если только вы не реализуете коллекцию (например, vector).

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

Является ли конструктор вызовом res. Интенсивно, чем вызов функции, которая делает то же самое (void a (...))?

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

Деструктор вызывается, когда я вызываю конструктор для уже созданного объекта?

если вы явно конструируете объект таким образом, тогда ответ «нет». ваша задача - правильно связать явные вызовы. Вы не можете позволить себе реализовать это неправильно, это так же хорошо, как UB. заказ всегда construct->destruct.

просто чтобы уяснить синтаксис:

// explicit ctor
new(ptr) T(ctorArgument);
// explicit dtor
ptr->~T();
1 голос
/ 18 февраля 2011

В вашей программе есть синтетические ошибки, модифицирующие ваш фрагмент -

struct F {

    int a,b,c,d;

    F(int _a, int _b) {a = _a; b = _b}; 

    void a(int _a, int _b) {a = _a; b = _b};

};

F f = F(5,6); // Here the default copy constructor gets called.
              // i.e., F( const F& obj ) ;

f = F(7,8);   // A temporary is created is assigned to `f` through default copy assignment operator.
              // i.e., F& operator=( const F& obj );

Вызывается деструктор, когда сконструированный объект выходит из области видимости (т. Е. Когда время жизни объекта завершается).

0 голосов
/ 18 февраля 2011

В вашем классе F нет конструктора, а есть только метод f. Таким образом, конструктор не вызывается в любое время.

...