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
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.
- eightThe 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
- oneAnd 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
- oneI once enjoyed the last
snippet'a
from the last answer in stackoverflow.com/questions/7458110/β¦. Using different approaches for assignment usingenable_if
andis_nothrow_move_assignable
is super-conceptually and very much in the spirit of modernC++.
- 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)
checkif (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
.