I need to implement an assignment operator for a class with a lot of members which I don't want to assign manually. Can I first make a shallow memory copy and then perform the necessary initializations?
class C
{
public:
C &operator=(const C &rhs)
{
if (&rhs == this)
return *this;
memcpy(this, &rhs, sizeof(C));
Init(rhs);
return *this;
}
.........
};
Thanks.
No. Unless the object has a POD type, this is undefined behavior. And
a user defined assignment operator means that it's not a POD. And in
practice, it could fail for a number of reasons.
One possible solution is to define a nested POD type with the data
members, and simply assign it, e.g.:
class C
{
struct Data { /* ... */ };
Data myData;
public:
C& operator=( C const& other )
{
myData = other.myData;
return *this;
}
};
Of course, that means that you need to constantly refer to each member
as myData.x, rather than simply x.
Well you could, but all your copied pointer members(if any) will then point to the same object and if that object goes out of scope, You would be left with a dangling pointer for all other objects which refer to it.
You are trying to dereference a C++ reference using the * operator. Unless you have operator* defined for this class it's not gonna work.
I mean in line
memcpy(this, *rhs, sizeof(C));
Related
I have to write one new class with members something like
class A
{
static int i;
const int j;
char * str;
...
...
};
Now I want to write assignment operator for it.
A& A::operator=(const A& rhs)
{
if(this != rhs)
{
delete [] str;
str = new char[strlen(rhs.str)+1];
strcpy(str, rhs.str);
}
return * this;
}
Is it right? I shall ignore the static and const members(?).
The assignment operator copies one object into another. Since all objects of the same type share the same static members, there's no reason to copy the static members.
const members are another matter. You can't (well, shouldn't) change them, but if two objects have different values for a const member, it might not be a good idea to copy one object into another; the const member won't have the right value. That's why the compiler doesn't generate a copy assignment operator for classes that have const members. So you first have to be sure that copying such an object makes sense, even with the wrong value for the const members; and then ask yourself why it has const members if they don't affect how it behaves.
References too. (yes, there's an echo in here)
Static members don't belong to a single object, so you don't need to copy those.
const members can't be changed, so you really can't do a proper copy. References too.
I'm working on my assignment in C++ course.
I have to create operator+= which will add an object to another set of object.
So, how do I implement operator+= here?
class classNew
{
anotherClass *my_objects;
public:
// TODO: classNew(int, char const *)
classNew operator+=(const anotherClass & rhs);
};
int main()
{
classNew d1(7, "w");
anotherClass sgs[5];
// somehow init sgs[0]..[4]?
for (int i=0; i<sizeof(sgs)/sizeof(*sgs); ++i)
d1 += sgs[i];
}
UPDATE:
I have something like this
newClass newClass::operator+=(const anotherClass& seg){
this->my_objs[n_seg] = seg;
return *this;
}
Unless your operator+= is intended to modify the object you're adding, which would be highly unusual, I'd suggest one two simple changes to the signature:
classNew & classNew::operator+=(const anotherClass& rhs);
You always want to return a reference to the class, otherwise you get a copy.
You have a pointer to anotherClass in your class, I assume that this is actually a pointer to an array. You simply have to copy the passed rhs to the appropriate spot in your array, reallocating it and growing it if necessary. If my assumption is incorrect, you just need to do whatever addition is defined as for your class.
If this weren't an assignment I would also suggest replacing the pointer with a std::vector<anotherClass>.
I have a class called Location and I needed to add a CArray to its member variables. This change caused the need to overload the assignment operator.
Is there a way to copy all of the variables in this class type that were being copied before I made the change and just add the additional code to copy the CArray without copying every single member variable individually?
Location& Location::operator=(const Location &rhs)
{
// Only do assignment if RHS is a different object from this.
if (this != &rhs)
{
//Copy CArray
m_LocationsToSkip.Copy(rhs.m_LocationsToSkip);
//Copy rest of member variables
//I'd prefer not to do the following
var1 = rhs.var1;
var2 = rhs.var2;
//etc
}
return *this;
}
Yes, sort of. Use a type that overloads operator= itself, so you don't have to do it in the containing class instead. Even when writing MFC code, I still mostly use std::vector, std::string, etc., instead of the MFC collection and string classes. Sometimes you're pretty much stuck using CString, but I can't recall the last time I used CArray instead of std::vector.
Yes. What I usually do is to put everything in a Members struct in the class except what is not copyable. Like this:
class Location
{
struct Members
{
int var1, var2;
};
Members m;
CArray m_LocationsToSkip;
public:
Location& operator=(Location const& rhs);
};
Location& Location::operator=(const Location &rhs)
{
// Only do assignment if RHS is a different object from this.
if (this != &rhs)
{
//Copy CArray
m_LocationsToSkip.Copy(rhs.m_LocationsToSkip);
//Copy rest of member variables
m = rhs.m; //will use Members automatically generated operator=
//which should do the correct thing because you only put
//normally copyable members in m
}
return *this;
}
I first posted about this here: https://stackoverflow.com/questions/469696/what-is-your-most-useful-c-c-utility/1609496#1609496
No you cant.
Best way to do this is use script to generate real code.
This is usually done with what's called "copy and swap idiom". You implement a copy constructor and a swap() method that exchanges member values and, most importantly, pointers to external data. With that your assignment operator looks like:
C& C::operator=( const C& c ) {
C tmp( c );
this->swap( tmp );
return *this;
}
You don't even need a self-assignment guard with this.
In short:
How do I define operator= for my class such that it compiles only if rhs is 0 (obj = 0)
but gives compilation error if rhs is non-0 value.
I know it is possible, forgot how.
Longer:
I have class C. I want to allow assignment obj = 0 for object of this class (which means, reset the object), but assignment from no other integer or pointer is defined. No conversion from integer or from pointer is defined other than obj=0.
C obj;
obj = 0; // reset object
Inside the operator=, I can do assert(rhs == 0), but that's not good enough.
I know it is possible
to define operator= such that
it gives compilation error if rhs is not 0. Forgot details.
Can anybody refill ?
Thanks
Use a pointer-to-member:
class foo
{
// Give it a meaningful name so that the error message is nice
struct rhs_must_be_zero {};
// The default operator= will still exist. If you want to
// disable it as well, make it private (and the copy constructor as well
// while we're at it).
foo(const foo&);
void operator=(const foo&);
public:
foo& operator=(int rhs_must_be_zero::*) { return *this; }
};
Since you can't access foo::rhs_must_be_zero, you cannot name a pointer to member from this class. The only pointer to member you can name is the null pointer, aka the literal zero.
Demo at http://ideone.com/bT02z
Consider the following:
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
vector<A> vec;
vec.push_back(foo); // <-- compile error!
return 0;
}
Obviously, the copy constructor is not enough. What am I missing?
EDIT:
Ofc. I cannot change this->c in operator=() method, so I don't see how operator=() would be used (although required by std::vector).
I'm not sure why nobody said it, but the correct answer is to drop the const, or store A*'s in the vector (using the appropriate smart pointer).
You can give your class terrible semantics by having "copy" invoke UB or doing nothing (and therefore not being a copy), but why all this trouble dancing around UB and bad code? What do you get by making that const? (Hint: Nothing.) Your problem is conceptual: If a class has a const member, the class is const. Objects that are const, fundamentally, cannot be assigned.
Just make it a non-const private, and expose its value immutably. To users, this is equivalent, const-wise. It allows the implicitly generated functions to work just fine.
An STL container element must be copy-constructible and assignable1(which your class A isn't). You need to overload operator =.
1
: §23.1 says The type of objects stored in these components must meet the requirements of CopyConstructible
types (20.1.3), and the additional requirements of Assignabletypes
EDIT :
Disclaimer: I am not sure whether the following piece of code is 100% safe. If it invokes UB or something please let me know.
A& operator=(const A& assign)
{
*const_cast<int*> (&c)= assign.c;
return *this;
}
EDIT 2
I think the above code snippet invokes Undefined Behaviour because trying to cast away the const-ness of a const qualified variable invokes UB.
You're missing an assignment operator (or copy assignment operator), one of the big three.
The stored type must meet the CopyConstructible and Assignable requirements, which means that operator= is needed too.
Probably the assignment operator. The compiler normally generates a default one for you, but that feature is disabled since your class has non-trivial copy semantics.
I think the STL implementation of vector functions you are using require an assignment operator (refer Prasoon's quote from the Standard). However as per the quote below, since the assignment operator in your code is implicitly defined (since it is not defined explicitly), your program is ill-formed due to the fact that your class also has a const non static data member.
C++03
$12.8/12 - "An implicitly-declared
copy assignment operator is implicitly
defined when an object of its class
type is assigned a value of its class
type or a value of a class type
derived from its class type. A program
is illformed if the class for which a
copy assignment operator is implicitly
defined has:
— a nonstatic data member of const type, or
— a nonstatic data
member of reference type, or
— a
nonstatic data member of class type
(or array thereof) with an
inaccessible copy assignment operator,
or
— a base class with an inaccessible
copy assignment operator.
Workaround without const_cast.
A& operator=(const A& right)
{
if (this == &right) return *this;
this->~A();
new (this) A(right);
return *this;
}
I recently ran into the same situation and I used a std::set instead, because its mechanism for adding an element (insert) does not require the = operator (uses the < operator), unlike vector's mechanism (push_back).
If performance is a problem you may try unordered_set or something else similar.
You also need to implement a copy constructor, which will look like this:
class A {
public:
const int c; // must not be modified!
A(int _c)
...
A(const A& copy)
...
A& operator=(const A& rhs)
{
int * p_writable_c = const_cast<int *>(&c);
*p_writable_c = rhs.c;
return *this;
}
};
The special const_cast template takes a pointer type and casts it back to a writeable form, for occasions such as this.
It should be noted that const_cast is not always safe to use, see here.
I just want to point out that as of C++11 and later, the original code in the question compiles just fine! No errors at all. However, vec.emplace_back() would be a better call, as it uses "placement new" internally and is therefore more efficient, copy-constructing the object right into the memory at the end of the vector rather than having an additional, intermediate copy.
cppreference states (emphasis added):
std::vector<T,Allocator>::emplace_back
Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.
Here's a quick demo showing that both vec.push_back() and vec.emplace_back() work just fine now.
Run it here: https://onlinegdb.com/BkFkja6ED.
#include <cstdio>
#include <vector>
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
// Copy constructor
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
A foo2(999);
std::vector<A> vec;
vec.push_back(foo); // works!
vec.emplace_back(foo2); // also works!
for (size_t i = 0; i < vec.size(); i++)
{
printf("vec[%lu].c = %i\n", i, vec[i].c);
}
return 0;
}
Output:
vec[0].c = 1337
vec[1].c = 999