I can not figure out how to overload the assignment operator. With binary operators, more or less everything is clear, at least there are two operands, but with this there is no way. Could you give an example of the overload "=", and explain what does where, and the result of the overload? For example, to overload so that it adds + 5 to the assigned number, or something like, and to show to what and from what it is assigned and to where it is added.

  • @VladD, since you’ve transferred everything back, I’ll erase our comments here. - avp

4 answers 4

Vector& Vector::operator=(Vector& v)//ΠΏΠ΅Ρ€Π΅Π³Ρ€ΡƒΠ·ΠΊΠ° { x=vx;y=vy;z=vz; return *this;//Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ ссылку Π½Π° Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ } 

I do not know what caused the doubts and questions. The main thing after assignment is to return a reference to the current object.

  • eight
    The main thing to remember to check for assignment is to yourself and it would be better to transfer the assigned object by a constant link, so as not to accidentally change it (which does not correspond to the semantics of assignment) - Dith
  • one
    And the function parameter should be done with the const Vector & Vector :: operator = (const Vector & v) modifier - skegg
  • honestly speaking, it is not clear to me why to check for assignment to oneself. Will it not work? Specifically, everything is perfect here at all .... - Andrio Skur

Fashionable version in the spirit of C ++ 11 using the exchange and transfer semantics:

 class C { public: C(std::string someName) : name(someName) {} void swap(C& other) { name.swap(other.name); } C(const C& other) : name(other.name) // конструктор копирования ΠΈΠ· lvalue { } C(C&& other) // конструктор копирования ΠΈΠ· rvalue, ΠΎΠ½ ΠΆΠ΅ конструктор пСрСноса { this->swap(other); } C& operator=(C other) // ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ присваивания { // ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΏΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΡŽ Π²Π°ΠΆΠ½Π°! this->swap(other); // ΠΎΠ±ΠΌΠ΅Π½ с Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ ΠΊΠΎΠΏΠΈΠ΅ΠΉ return *this; } private: std::string name; }; 

(comments run out here)

@avp :
IMHO the farther, the C ++ becomes incomprehensible.

Apparently from the language for practical programming, it will soon become a language for a certain caste of hackers making tools for making tools.

The question here is how many ordinary programmers can at least use (I'm not talking about modification) the final tools they have created.


@VladD :
C ++ is more complicated, that's a fact. In addition, there are more native lambda language and a whole bunch.

From my practice, the semantics of shallow-copying objects (kak eto po-russki?), Which is generated by default for C ++ objects, is almost never needed. IMHO semantics of noncopyable by default would be much better.


@avp :
@VladD , after replacement

 C(C&& other) 

on

 C(C& other) 

happened

 avp @avp -xub11:~/src/dispro$ g++ -std=c++0x t.cpp avp @avp -xub11:~/src/dispro$ ./a.out swap, other = (0xbfce8970) c1 -> c2 -> c1 avp @avp -xub11:~/src/dispro$ 

So it was intended? Something I do not see the point here.

By the way, the question was about the operator = .


@VladD :
@avp : No, it was conceived differently. Must be

 C(const C& other) : name(other.name) // конструктор копирования ΠΈΠ· lvalue { } 

(I have so in the answer) It should work correctly.

And about the assignment statement, the code is first copied, the other in the statement is already a copy. So, she can "steal" the value from her and leave her to die at the end of the function. The idea is that copying is still unavoidable, and transferring is a cheap operation.


@avp :
@VladD , I understood that. Has corrected the designer, as in the answer and earned.

Interestingly, is swap used for efficiency? Just a pointer exchange?

All this is so deeply buried that I just want to give up on these innovations and not take on the work of those who write like that.


@VladD :
@avp : the idea is that the swap is written once, and the rest of the functions / operators use it whenever possible. This reduces the repetition of the code. Plus, swap should not give exceptions, EMNIP, with exception-safety arising from it.


@avp :
@VladD , it’s clear about swap once, too. Its meaning is not quite clear.

See, is the string passed to the constructor by value? If so, then the bytes are still copied. Then there is an exchange of pointers with a temporary copy and apparently sometime a destroyer will be called to the temporary copy. So?

How is this better than sending a reference to the constructor and copying bytes in it?

Or am I missing something fundamental?


@VladD :
@avp : no better, just think less: all operations are expressed in a standard way through swap. See: old data must be deployed. We trust this temporary copy destructor, and do not write it yourself. Similarly, in the assignment operator creating a copy, we trust the copy constructor, rather than writing it ourselves.


@avp :
@VladD , why, when sending a link to something, deallocate?

IMHO, when passed by value, simply adds unnecessary calls to the constructor and destructor of the temporary copy.

Somewhere I am wrong?

Some strange approach.


@avp : I guess I didn't describe it very well. See: the object o1 is called an assignment operator with the argument o2. At the same time, he must deploy his old data, and in their place copy the data from o2. If the transfer is declared by value, then a temporary copy of o3 comes to us. All possible exceptions are thrown in the copy constructor. In order not to write again the allocation of old data and the movement of new ones from o3, we use the swap and o3 destructor. Writing in the traditional way would be a bit more efficient (but modern compilers should be optimized), but there would be a dubbing of the code with the transfer constructor. (A transfer constructor is needed to effectively execute code like C c(f()); ;, without unnecessary copying.)

  • @VladD, I have g ++. Real (Ubuntu / Linaro 4.6.3-1ubuntu5) 4.6.3 doesn't even compile (\ #include <string> inserted). Could you please explain in more detail what this class should do? And if you can examples of its use. - avp
  • @avp: strange, I compiled in C ++ 0x mode: ideone.com/E5ZK8G The class is just a string container, but with an assignment operator of interest to the TS. (Well, and the constructor of copying to the heap.) - VladD
  • @VladD, and your url is the same avp @ avp-xub11: ~ / src / dispro $ g ++ -c -std = C ++ 0x t.cpp cc1plus: error: unrecognized command line option "-std = C ++ 0x "Avp @ avp-xub11: ~ / src / dispro $ avp @ avp-xub11: ~ / src / dispro $ g ++ -c -std = c ++ 0x t.cpp t.cpp: In the function" int main (int, char **) ": t.cpp: 62: 12: error: cannot bind" C "lvalue to" C && "t.cpp: 45: 5: error: initializing argument 1 of" C :: C (C &&) "avp @ avp-xub11: ~ / src / dispro $ and yet, how should the class work? - avp
  • @avp: hmm, weird. The idea was to have a code like this: C f () {return C (); } C c (f ()); Since the rvalue reference comes to us, we "take" its value from it. Now I will clarify what the problem is with the compilation on older versions. - VladD
  • one
    I once enjoyed the last snippet'a from the last answer in stackoverflow.com/questions/7458110/…. Using different approaches for assignment using enable_if and is_nothrow_move_assignable is super-conceptually and very much in the spirit of modern C++. - Costantino Rupert

Many years ago I found such a universal option: when assigning, call the destructor of the old object and initialize this place with a new object.

 #include <iostream> class T { int x; public: T(int _x=0): x(_x) { std::cerr<<"T(int "<<x<<")\n"; } T(const T& t): x(tx) { std::cerr<<"T(const &T "<<x<<")\n"; } ~T() { std::cerr<<"~T("<<x<<")\n"; } const T & operator=(const T& t) { if (this != &t) { this->~T(); new (this) T(t); } return *this; } friend class C; }; class C { T x; public: C(T _x=0): x(_x) { std::cerr<<"C(T "<<xx<<")\n"; } C(const C& c): x(cx) { std::cerr<<"C(const &C "<<xx<<")\n"; } ~C() { std::cerr<<"~C("<<xx<<")\n"; } }; int main() { std::cerr<<__LINE__<<'\n'; T a(1), b(2); std::cerr<<__LINE__<<'\n'; C x(a), y(b); std::cerr<<__LINE__<<'\n'; a=b; std::cerr<<__LINE__<<'\n'; x=y; std::cerr<<__LINE__<<'\n'; } 

running this example (using gcc):

 38 T(int 1) T(int 2) 40 T(const &T 1) T(const &T 1) C(T 1) ~T(1) T(const &T 2) T(const &T 2) C(T 2) ~T(2) 42 ~T(1) T(const &T 2) 44 ~T(1) T(const &T 2) 46 ~C(2) ~T(2) ~C(2) ~T(2) ~T(2) ~T(2) 

Line 41: The constructor T is called first to pass the argument to the constructor C, where the class member is initialized by the object, then the destructor is called for the argument. If in the constructor C put instead of "T _x" "const T & _x", you can avoid unnecessary calls to the constructor and destructor.

Line 43: Our assignment operator worked for T

Line 45: The default assignment statement for C causes assignment for class members.

  • one
    @sercxjo: you forgot the if (this != &t) check if (this != &t) . - VladD
  • @VladD Exactly. But it is not clear why the compiler creates an intermediate object when passing a and b by reference. ah, I confused it, now I will correct it again - sercxjo

The minimum assignment operator is

 void Cls::operator=(Cls other) { swap(*this, other); } 

According to the standard, this is a copy assignment operator.
However, it can also perform a move if Cls has a move constructor:

 Cls a, b; a = std::move(b); // Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠΊΠ°ΠΊ // Cls other(std::move(b)); a.operator=(other); // ^^^^^^^^^^ // ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Ρ‰Π΅Π½ΠΈΠ΅: Π²Ρ‹Π·ΠΎΠ² Cls::Cls(Cls&&) 

After the swap, the current members of the class are in a temporary object of other and are removed when leaving the assignment operator.
When copying an assignment to yourself, an extra copy will be made, but there will be no errors.

The type of result can be any.
An automatically generated assignment operator has a return type of Cls& and returns *this . This allows you to write code of the form a = b = c or (a = b) > c .
But many code style conventions do not approve of this, in particular, see CppCoreGuidelines ES.expr "Avoid complicated expressions" .

This assignment operator requires copy / move constructors and a swap function.
Together it looks like this:

 class Cls { public: Cls() {} // ΠšΠΎΠ½ΡΡ‚Ρ€ΡƒΠΊΡ‚ΠΎΡ€ копирования Cls(const Cls& other) : x(other.x), y(other.y) {} // ΠšΠΎΠ½ΡΡ‚Ρ€ΡƒΠΊΡ‚ΠΎΡ€ пСрСмСщСния Cls(Cls&& other) noexcept { swap(*this, other); } // ΠžΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ присваивания void operator=(Cls other) noexcept { swap(*this, other); } // ОбмСн friend void swap(Cls& a, Cls& b) noexcept { using std::swap; // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ стандартной Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² список ΠΏΠ΅Ρ€Π΅Π³Ρ€ΡƒΠ·ΠΎΠΊ... swap(ax, bx); // ... ΠΈ Π²Ρ‹Π·ΠΎΠ² с использованиСм поиска ΠΏΠΎ Ρ‚ΠΈΠΏΠ°ΠΌ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² (ADL). swap(ay, by); } private: X x; Y y; }; 

The copy constructor copies each member of the class.

The relocation constructor constructs empty class members, and then exchanges them with its argument. You can move each member individually, but it is more convenient to use swap .

The swap function can be a free function friend. Many algorithms expect the free function swap , and call it through a search by argument type (ADL).
Previously, it was also recommended to write the swap method so that you can write f().swap(x); , but with the advent of the semantics of moving, this became unnecessary.

If functions cannot throw exceptions, they should be marked as noexcept . This is needed for std::move_if_noexcept and other functions that can use more efficient code if the assignment or constructor throws no exceptions. For such a class, <type_traits> issued

 std::is_nothrow_copy_constructible<Cls> == 0 std::is_nothrow_move_constructible<Cls> == 1 std::is_nothrow_copy_assignable<Cls> == 0 std::is_nothrow_move_assignable<Cls> == 1 

Although the assignment statement is marked as noexcept , when called with the argument const Cls& a copy will occur that may throw an exception. Therefore, is_nothrow_copy_assignable returns false .