Consider the following simplified example of a class holding a shared resource:
class array
{
typedef std::array<float, 1000> resource;
public:
// default constructor, creates resource
array() : p_(std::make_shared<resource>()), a_(0), b_(1000) {}
// create view
array operator()(int a, int b) { return array(p_, a_+a, a_+b); }
// get element
float& operator[](int i) { return (*p_)[a_+i]; }
private:
std::shared_ptr<resource> p_;
int a_, b_;
// constructor for views
array(std::shared_ptr<resource> p, int a, int b) : p_(p), a_(a), b_(b) {}
};
Now I'm wondering how to define a semantics for this class that doesn't confuse its users. For example, I'd like to allow operator=() to copy elements:
array x, y;
x = y; // copies all elements from y's storage to x's
x(30,40) = y(50,60); // copies 10 elements
But then, to be consistent, shouldn't the copy constructor and the copy assignment operator always copy? What about:
array z = x(80,90); // will create an array referencing x's storage
In this case, the copy will be elided by the compiler, so no matter what my copy assignment operator does, z will hold a reference to x's storage. There's no way around this.
So does it make more sense for assignment to always create a reference, and copying to be declared explicitly? For example, I could define a wrapper class copy_wrapper, assignment of which forces copying of elements:
class copy_wrapper
{
array& a_;
public:
explicit copy_wrapper(array& a) : a_(a) {}
array& get() { return a_; }
};
class array
{
// ...
array& operator=(const array& a); // references
array& operator=(copy_wrapper c); // copies
copy_wrapper copy() { return copy_wrapper(*this); }
};
This would force users to write:
array x, y;
x(30,40) = y(50,60).copy(); // ok, copies
x(30,40) = y(50,60); // should this throw a runtime error?
x = y; // surprise! x's resource is destructed.
A bit cumbersome, and worse than that: not what you expect.
How should I deal with this ambiguity?
Does it make more sense for assignment to always create a reference, and copying to be declared explicitly?
In practice, no. Expecting developers to remember that a = b will not copy is what got std::auto_ptr into hot water. The C++11 standards committee also decided on std::move and std::forward to allow developers to explicitly say "I'm not making copies."
What about: array z = x(80,90); In this case, the copy will be elided by the compiler, so no matter what my copy assignment operator does, z will hold a reference to x's storage. There's no way around this.
This was your giveaway. You want "array" and "view to an array" to act differently, but you're representing them both with class array.
Implement a new class arrayview with a similar public interface and move the ranges a_ and b_ to arrayview.
array x, y;
x = y; // "array = array" copies all elements
x(30,40) = y(50,60); // "arrayview = arrayview" copies element range
arrayview v = x(80,90); // "arrayview(const arrayview&) shallow copies
x(30,40) = y; // NOT DEFINED, since this has no clear meaning
array z = x(80,90); // NOT DEFINED, since this has no clear meaning
Note that in your arrayview assignment operator, it should be valid to copy from an arrayview with the same resource, but in that specific case you should check whether std::copy_backward is needed instead of std::copy.
Related
I am an beginner in C++ and would like to do something like this:
myObj f(){
// do stuff
// return instance of myObj
}
int main(){
// do stuff
myObj mO = f();
}
What would I have to do, to make this work in C++?
My thoughts were, that I would have to implement an assign operator for the myObj stuct/class or to write another constructor that looks like this myObj::myObj(myObj mO){...}, which I use like this myObj = myObj(f());.
Is this correct?
Do I have to do more, to make this work?
Could you maybe provide a working example?
Thanks!
That would almost compile as-is.
//define a class
class myObj {};
// return an instance of the class
myObj f() {
return myObj{};
}
// call with the same main as in the question:
int main(){
// do stuff
myObj mO = f();
}
C++ defines for you a copy constructor, an assignment operator and a move constructor if this can be done trivially; in these cases you should have to do nothing, just return an object instance and the caller will get it.
If the object has however some parts that cannot be copied (for example references) then you need to provide copy constructors and assignment yourself (but may be the class indeed shouldn't be copied or assigned).
There are also other limitations that prevent automatic synthesis of the move constructor (to avoid bugs).
Note also that there are cases in which the C++ compiler will synthesize copy constructor and assignment, but using wrong code. You need to be careful (for example if the class contains naked owning pointers).
For a simple case in which everything works out of the box with no need to do anything consider:
// A bi-dimensional point
struct P2d {
double x, y;
};
// Computes the middle point given two points
P2d average(P2d a, P2d b) {
return P2d{(a.x+b.x)/2, (a.y+b.y)/2};
}
As you see nothing is needed in the class to support returning P2d values or accepting P2d parameters.
The compiler in that case automatically completes the definition code to something like:
struct P2d {
double x, y;
P2d(const P2d& other)
: x(other.x), y(other.y)
{
}
P2d& operator=(const P2d& other) {
x = other.x;
y = other.y;
return *this;
}
~P2d() {
}
};
I have a class that looks somewhat like this:
class S
{
public:
int* data;
S() : data(new int[10]) {}
};
The constructor allocates the memory of 10 integers, and the default copy constructor as expected merely copies the pointer itself rather than the content.
Even if there is an instance of S that has const modifier, I can modify the data that data points to, since that data itself does not have const modifier. I could avoid this by making data private and only allowing write access via a non-const method like so:
class S
{
private:
int* data;
public:
S() : data(new int[10]) {}
int& operator(size_t i)
{
return data[i];
}
const int& operator(size_t i) const
{
return data[i];
}
};
But now I can use the copy constructor to circumvent the constness of the instance of S like so:
void main()
{
const S a; // Allocates memory
S b(a); // Also points to memory allocated for a
b(1) = 3; // Writes to a even though it is not supposed to be mutable
}
What would be an elegant way to solve this problem (potentially using templates)?
The data pointed to by an instance of const S should not be mutable at all using only this instance.
Copy constructor should only copy pointer, but not make a deep copy of the data.
Both a const S and an S should be creatable via a copy constructor given an instance of S such that the const instance cannot modify the data, but the non-const instance can.
It is possible to know in the copy constructor if the object being copied is const by providing two different copy constructors, one which takes a const parameter and one which does not. The compiler will select whichever version matches the passed parameter. Set a flag in the constructor so it can throw an error when a non-const operation is performed.
The best way to avoid the leaked memory shown in the question is to used a smart pointer like std::shared_ptr rather than a raw pointer. Unfortunately shared_ptr is meant for single objects, not arrays; workarounds are possible as in this StackOverflow question. I'm not going to try to solve this now, the code below still has the leak.
To be complete you should follow the Rule of Three and provide an operator= and destructor as well. I left this as an exercise for the reader.
class S
{
private:
int* data;
bool is_const;
public:
S() : data(new int[10]), is_const(false) { data[1] = 42; }
S(const S& other) : data(other.data), is_const(true) {}
S(S& other) : data(other.data), is_const(false) {}
int& operator()(size_t i)
{
if (is_const)
throw std::logic_error("non-const operation attempted");
return data.ptr[i];
}
const int& operator()(size_t i) const
{
return data.ptr[i];
}
};
See it in action: http://ideone.com/SFN89M
Delete the copy constructor (and assignment operator) for S. Create a new proxy class (SCopy) that holds a pointer to an S object (which is passed in to the constructor for SCopy). SCopy would then implement the const int &operator() const and not the non-const version.
This would then allow you to implement a destructor in S that would free the memory you're currently leaking.
I have a simple class that contains a pointer to one of it's own members:
struct X {
int val;
int* pVal;
X(int v) : val(v), pVal(&val) {}
}
X x(1);
I have some code like this:
void foo() {
doStuffWith(x);
x = X(2); // completely discard the old value of X, and start again
doStuffWith(x);
}
I'm worried that when x is reassigned, x.pVal will invalidly point to the member of the temporary X(2) if return value optimization does not occur.
I realize I could write a copy constructor to fix this. However, it seems wasteful to do the copy in the first place, rather than constructing the object in the right spot in memory to begin with.
Is it reasonable to use the placement new operator here? Or does this have unintented consequences for destructors?
void foo() {
doStuffWith(x);
new (&x) X(2); // completely discard the old value of X, and start again
doStuffWith(x);
}
The most obvious (and probably most effective) way to make this work is to provide copy assignment and copy construction operators to "do the right thing", something on this general order:
struct X {
int val;
int* pVal;
X(int v) : val(v), pVal(&val) {}
X(X const &other) : val(other.val), pVal(&val) {}
// pVal was already set by ctor, so just ignore it:
X &operator=(X const &other) { val = other.val; return *this; }
// and allow assignment directly from an int:
X &operator=(int n) { val = n; return *this; }
};
Then the rest of the code can just copy/assign X objects without jumping through hoops to prevent corruption.
No
It won't destruct the old value of x, but you're right the the default copy assignment operator won't do what you want either.
For me it seems quite acceptable solution if X's destructor will be called before placement new. It's syntactically allowed even the destructor is not actually specified for the class.
struct X {
int val;
int* pVal;
X(int v) : val(v), pVal(&val) {}
};
X x(1);
void foo() {
doStuffWith(x);
x.~X();
new (&x) X(2);
doStuffWith(x);
}
In this form it is a correct way to reuse memory for any object (but only if the object's ctor can't throw! otherwise UB may happen on program shutdown, i.e. double call of the destructor).
Indeed, the equality of pointers passed and returned from placement new in it non-array form is guaranteed by the standard:
18.6.1.3 Placement forms
...
void* operator new(std::size_t size, void* ptr) noexcept;
Returns: ptr.
Remarks: Intentionally performs no other action.
(and the result of the conversion to void* and then back to the same
pointer type is also guaranteed to be the same as the source pointer)
However, to avoid non-proper use of the class it would be more safe either define copy assignment and copy constructor or declare this class as noncopyable (with deleted ones)
And only the last (noncopyable) case may be regarded as a reason for using placement new.
Although I'm far from promoting placement new for general use, it express the intention to reuse object memory directly and doesn't rely on any optimization. Copy constructor and copy assignment are, of course, more safe, but don't express this intention excactly: no "copy" actually needed, the new object should be constructed in place of the old one.
One of the purposes of a copy constructor is so that pointers declared in a copy point to their respective members, and not simply back to the originals members, but how exactly is this implemented?
say your constructor is:
foo::foo(int i)
{
blah = i;
bar = new whatever; //bar is a pointer to a whatever
}
so what should the copy constructor implementation look like? is the only thing you have to put in it something like:
bar = this->whatever;
or
bar = whatever;
or should it contain everything the normal constructor does?
It entirely depends on what a foo is, and its relation to a whatever.
Possible options are:
Sharing the object
foo::foo(const foo& other) : blah(other.blah), bar(other.bar)
{ }
Creating another object
foo::foo(const foo& other) : blah(other.blah), bar(new whatever)
{ }
Creating a new copy of the object
foo::foo(const foo& other) : blah(other.blah), bar(new whatever(*other.bar))
{ }
No, copy constructor is about copying members of the object, it doesn't have much to do with pointers, unless the members are. Like if you have an object point with x and y members, you would make a copy constructor
point(const point& p) : x(p.x), y(p.y) { }
and it may have another constructor like
point(int x_,int y_) : x(x_), y(y_) { }
If you have a pointer member, it depends on the object and how you want to handle it — you may want to copy the pointer or create another pointer, etc. Depends on the data it is pointing to.
If you don't have a copy constructor, a default one will be created for you which does a shallow copy, where each member variable of one object is simply assigned to the other, as opposed to a deep copy.
Therefore if you have a pointer to a dynamically allocated buffer, then the default copy constructor is going to simply assign that pointer to the copy's pointer, rather than creating it's own new buffer to point to and copying the contents of the buffer into the new one. eg.
class DynamicIntArray {
private:
int *_array;
size_t _size;
public:
DynamicIntArray(size_t size) : _array(new int[size]), _size(size) { }
DynamicIntArray (const DynamicIntArray &d) // copy constructor
{
delete[] _array;
_array = new int[d._size];
_size = d._size;
std::copy(_array, d._array, d._array + d._size);
}
/* destructor, assignment operator, etc */
};
If you didn't create a default copy constructor, then the default one created would simply assign d._array to _array, which would cause serious problems. ie. Instead of the above, it would do:
_array = d._array;
_size = d._size;
Note that if you have a copy constructor, you should probably have an assignment operator and a destructor (look up the rule-of-three).
The questions I have are NOT homework questions, but I am considering using these concepts in my assignment. The context, if it helps, is like this: I need to keep track of several union instances and they belong to my own union in one of my own classes as class variables. (Note: the number of union instances is unknown, so I cannot just have a fixed number of union instances.
Q1: If I have a union, say MyUnion,
and many instances of this union,
can I then put them into a vector
like
vector<union MyUnion> myVector(10);
Q2: Is it valid to have a pointer of
union? Like
union MyUnion *myUnionPtr = new union myUnion;
Q3: I am considering using a vector
of union pointers in my
implementation, is this concept
correct? Also, is it a normal
approach in C++? Do I need to
rethink about my design?
If the union is CopyConstructable and Assignable, then it meets the requirements for std::vector.
Yes: MyUnion* ptr = new MyUnion();
A container of pointers works in some situations, but if you want a container of owning pointers, look at Boost's ptr_* containers. However, here it appears you'd either have a container of non-pointers or a container of non-owning pointers, either of which is fine and common for vector.
All types are, by default, both CopyConstructable and Assignable. ("Copyable" is used to mean the union of these concepts, however the standard specifies them individually.) This is because copy ctors and op= are added to classes (unions are one class-type) except under certain circumstances. There are a few references online, but I'm not aware of a single one, available freely online, that spells these out.
You have to go out of your way to prevent it, such as:
make the copy ctor or op= non-public
make the copy ctor or op= take a non-const reference
give the class type a non-CopyConstructable or non-Assignable member
Example:
union CopyableUnion {
int n;
char c;
double d;
};
union NonCopyableUnion {
int n;
char c;
double d;
NonCopyableUnion() {} // required, because any user-defined ctor,
// such as the private copy ctor below, prevents the supplied
// default ctor
private:
NonCopyableUnion(NonCopyableUnion const&);
NonCopyableUnion& operator=(NonCopyableUnion const&);
};
int main() {
CopyableUnion a;
CopyableUnion b = a; // fine, uses copy ctor
b = a; // fine, uses op=
NonCopyableUnion c;
NonCopyableUnion d = c; // compile error (copy ctor)
d = c; // compile error (op=)
return 0;
}
Note: And just because something is Copyable, doesn't mean it does what you want! Example:
struct A {
int* p;
A() : p(new int()) {}
// the provided copy ctor does this:
//A(A const& other) : p(other.p) {}
// which is known as "member-wise" copying
~A() { delete p; }
};
int main() {
A a;
{
A b = a;
assert(b.p == a.p); // this is a problem!
} // because when 'b' is destroyed, it deletes the same pointer
// as 'a' holds
return 0; // and now you have Undefined Behavior when
// ~A tries to delete it again
}
The same thing holds true for unions, of course. The fix applies equally, though, as well:
struct A {
int* p;
A() : p(new int()) {}
A(A const& other) : p(new int(*other.p)) {}
~A() { delete p; }
};
(If you spotted it, yes, A has a problem if you ever try to use op=, in just the same way as it originally had with the copy ctor.)