2016-05-15 3 views
4

Рассмотрят этот простой код:не может объяснить поведение компилятора

class A { 
public: 
    int value; 
    A(int value) { this->value = value; } 
    ~A() { printf("destroying %d\n", value); } 
    A operator ++() { return A(value + 1); } 
}; 

int main() { 
    A a(1); 
    printf("before increment: %d\n", a.value); 
    a = ++a; 
    printf("after increment: %d\n", a.value); 
    return 0; 
} 

Это выходы:

Перед приращением: 1
разрушающего 2
после приращения: 2
разрушающего 2

Почему значение a еще 1 перед уничтожением?

+0

Вы имеете в виду первое или второе разрушение? –

+0

Ваш 'operator ++()' должен возвращать ссылку (самому себе) после приращения самого себя, а не создавать новую временную 'A'. Как написано, этот код будет путать ад с пользователей класса. –

ответ

4

В методе operator ++ вы создаете временный объект A, который затем уничтожается, когда вы возвращаете его из функции. Должна быть и другая копия строительства и уничтожения, но RVO elides этот.

Когда вы добавляете журнал в конструктор, вы также увидите, что происходит дальше. Я также позволил себе изменить printf на cout, для более стильного стиля C++ ish.

#include <iostream> 

class A { 
public: 
    int value; 
    A(int value) { 
     std::cout << "creating " << value << std::endl; 
     this->value = value; 
    } 
    ~A() { 
     std::cout << "destroying " << value << std::endl; 
    } 
    A operator ++() { return A(value + 1); } 
}; 

int main() { 
    A a(1); 
    std::cout << "before increment: " << a.value << std::endl; 
    a = ++a; 
    std::cout << "after increment: " << a.value << std::endl; 

    return 0; 
} 

выход:

creating 1 
before increment: 1 
creating 2 
destroying 2 
after increment: 2 
destroying 2 

Вы можете также прочитать о канонических реализациях операторов перегрузки:

http://en.cppreference.com/w/cpp/language/operators

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

struct X 
{ 
    X& operator++() // prefix version 
    { 
     // actual increment takes place here 
     return *this; 
    } 
    X operator++(int) // postfix version 
    { 
     X tmp(*this); // copy 
     operator++(); // pre-increment 
     return tmp; // return old value 
    } 
}; 
+0

Хорошо, что вы упоминаете разумный способ реализации этих операторов.BTW, если OP реализовал его таким образом, ответ был бы (если бы серверы памяти меня правильно) неопределенным поведением из-за модификации одного и того же объекта в одном выражении с помощью 'a = ++ a;'. –

+0

Это выражение состоит из трех частей: (1) «a» (2) «++ a» (3) «присваивать (2) - (1)». (3) гарантированно произойдет после (1) и (2), поэтому нет, здесь нет неопределенного поведения. – tomekpe

+0

yes, но если '++ a' реализуется таким образом, который будет увеличиваться, то выражение приведет к неопределенному поведению. см. [this] (http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points) –

0

Это довольно просто:

a = ++a; 

Во-первых, создается новый временный A объект, который держит value из 2 (value + 1 = 2), этот объект перемещается/копируется в a, а затем уничтожен (который является первым destroying 2 принт вы смотрите). Теперь созданный компилятором конструктор move/copy просто выполняет memcopy членов, value в этом случае. value был 2 при присвоении темпа объекта, и таким образом avalue будет 2 тоже. Поэтому последние две цифры printf2 ничего удивительного.

2
int main() { 
    A a(1); // a.value = 1; 
    printf("before increment: %d\n", a.value); 
    a = ++a; // 1. create temporary object of type A with a.value+1 
      // 2. copy temporary object to object a. 
      // 3. destroy temporary object. 
    printf("after increment: %d\n", a.value); 
    return 0; 
} 

В принципе вы можете объявить оператор Преинкремент здесь постоянны для ясности

A operator ++() const { return A(value + 1); } 

Но ожидаемое поведение является:

A& operator ++() { ++value; return *this; } 
Смежные вопросы