Подтвердить что ты не робот

Почему оператор = работает над структурами, не будучи определенным?

Посмотрите на простой пример:

struct some_struct {
   std::string str;
   int a, b, c;
}

some_struct abc, abc_copy;
abc.str = "some text";
abc.a = 1;
abc.b = 2;
abc.c = 3;

abc_copy = abc;

Затем abc_copy является точной копией of abc.. как возможно без, определяющей оператор =?

(Это застало меня врасплох при работе над некоторым кодом..)

4b9b3361

Ответ 1

Если вы не определяете эти четыре метода (шесть в С++ 11), компилятор будет генерировать их для вас:

  • Конструктор по умолчанию
  • Конструктор копирования
  • Оператор присваивания
  • Destructor
  • Переместить конструктор (С++ 11)
  • Назначение перемещения (С++ 11)

Если вы хотите узнать, почему? Он должен поддерживать обратную совместимость с C (потому что C-структуры можно копировать с использованием = и в декларации). Но это также упрощает упрощение написания простых классов. Некоторые утверждают, что это добавляет проблемы из-за проблемы "мелкой копии". Мой аргумент против этого состоит в том, что у вас не должно быть класса с имеющимися в нем RAW-указателями. Используя соответствующие интеллектуальные указатели, проблема исчезнет.

Конструктор по умолчанию (если не определены другие конструкторы)

Созданный компилятором конструктор по умолчанию вызовет конструктор по умолчанию базовых классов, а затем каждый конструктор по умолчанию каждого члена (в том порядке, в котором они объявлены)

Деструктор (если не определено деструктор)

Вызывает деструктор каждого члена в обратном порядке объявления. Затем вызывает деструктор базового класса.

Копировать конструктор (если конструктор копирования не задан)

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

Оператор присваивания

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

Переместить конструктор (если конструктор перемещения не задан)

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

Оператор назначения переноса

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

Если вы определяете класс следующим образом:

struct some_struct: public some_base
{   
    std::string str1;
    int a;
    float b;
    char* c;
    std::string str2;
};

Что создаст компилятор:

struct some_struct: public some_base
{   
    std::string str1;
    int a;
    float b;
    char* c;
    std::string str2;

    // Conceptually two different versions of the default constructor are built
    // One is for value-initialization the other for zero-initialization
    // The one used depends on how the object is declared.
    //        some_struct* a = new some_struct;     // value-initialized
    //        some_struct* b = new some_struct();   // zero-initialized
    //        some_struct  c;                       // value-initialized
    //        some_struct  d = some_struct();       // zero-initialized
    // Note: Just because there are conceptually two constructors does not mean
    //       there are actually two built.

    // value-initialize version
    some_struct()
        : some_base()            // value-initialize base (if compiler generated)
        , str1()                 // has a normal constructor so just call it
        // PODS not initialized
        , str2()
   {}

    // zero-initialize version
    some_struct()
        : some_base()            // zero-initialize base (if compiler generated)
        , str1()                 // has a normal constructor so just call it.
        , a(0)
        , b(0)
        , c(0)   // 0 is NULL
        , str2()
        // Initialize all padding to zero
   {}

    some_struct(some_struct const& copy)
        : some_base(copy)
        , str1(copy.str1)
        , a(copy.a)
        , b(copy.b)
        , c(copy.c)
        , str2(copy.str2)
    {}

    some_struct& operator=(some_struct const& copy)
    {
        some_base::operator=(copy);
        str1 = copy.str1;
        a    = copy.a;
        b    = copy.b;
        c    = copy.c;
        str2 = copy.str2;
        return *this;
    }

    ~some_struct()
    {}
    // Note the below is pseudo code
    // Also note member destruction happens after user code.
    // In the compiler generated version the user code is empty
        : ~str2()
        // PODs don't have destructor
        , ~str1()
        , ~some_base();
    // End of destructor here.

    // In C++11 we also have Move constructor and move assignment.
    some_struct(some_struct&& copy)
                    //    ^^^^  Notice the double &&
        : some_base(std::move(copy))
        , str1(std::move(copy.str1))
        , a(std::move(copy.a))
        , b(std::move(copy.b))
        , c(std::move(copy.c))
        , str2(std::move(copy.str2))
    {}

    some_struct& operator=(some_struct&& copy)
                               //    ^^^^  Notice the double &&
    {
        some_base::operator=(std::move(copy));
        str1 = std::move(copy.str1);
        a    = std::move(copy.a);
        b    = std::move(copy.b);
        c    = std::move(copy.c);
        str2 = std::move(copy.str2);
        return *this;
    } 
};

Ответ 2

В С++ структуры эквивалентны классам, где члены по умолчанию используют открытый, а не частный доступ.

Компиляторы С++ также автоматически генерируют следующие специальные члены класса, если они не предоставляются:

  • Конструктор по умолчанию - никаких аргументов, значение по умолчанию активирует все.
  • Конструктор копирования - то есть метод с тем же именем, что и класс, который ссылается на другой объект того же класса. Копирует все значения.
  • Деструктор. Вызывается при уничтожении объекта. По умолчанию ничего не делает.
  • Оператор присваивания. Вызывается, когда одна структура/класс назначается другому. Это автоматически сгенерированный метод, который вызывается в приведенном выше случае.

Ответ 3

Но это определено. В стандарте. Если вы не укажете оператора =, вам будет предоставлен один. И оператор по умолчанию просто копирует каждую из переменных-членов. И как он знает, какой способ скопировать каждого участника? он вызывает их operator = (который, если не определен, предоставляется по умолчанию...).

Ответ 4

Это поведение необходимо для обеспечения совместимости источника с C.

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

Ответ 5

Оператор присваивания (operator=) является одной из неявно сгенерированных функций для структуры или класса в С++.

Вот ссылка, описывающая 4 неявно сгенерированных элемента:
http://www.cs.ucf.edu/~leavens/larchc++manual/lcpp_136.html

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

При необходимости неявно сгенерированная спецификация оператора присваивания является следующей. В спецификации указано, что результатом является назначаемый объект (self) и что значение абстрактного значения self в пост-состоянии self "совпадает с значением абстрактного значения аргумент from.

// @(#)$Id: default_assignment_op.lh,v 1.3 1998/08/27 22:42:13 leavens Exp $
#include "default_interfaces.lh"

T& T::operator = (const T& from) throw();
//@ behavior {
//@   requires assigned(from, any) /\ assigned(from\any, any);
//@   modifies self;
//@   ensures result = self /\ self" = from\any\any;
//@   ensures redundantly assigned(self, post) /\ assigned(self', post);
//           thus
//@   ensures redundantly assigned(result, post) /\ assigned(result', post);
//@ }

Ответ 6

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

Ответ 7

structs - это в основном конкатенация его компонентов в памяти (с некоторым возможным дополнением, встроенным для выравнивания). Когда вы присваиваете одной структуре значение другой, значения просто обрабатываются.