I have write following demo code to learn copy constructor and assignment operator. However there is a little confuse. I was told to delete pointers in assignment operator and allocate new address to the data. However I can only make my code work delete that line. I have taken this page as a reference, but it only shows the example of int while not the int*. How can I solve this problem?
#include <iostream>
#include <string>
#include <vector>
#include <random>
#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>
using namespace boost;
class ClassOne
{
public:
ClassOne():data(NULL) {}
ClassOne(int data_param):data(NULL)
{
init(data_param);
std::cout << "construct" << std::endl;
}
virtual ~ClassOne()
{
if (data) {
delete data;
}
data = NULL;
}
ClassOne(const ClassOne& rhs){
std::cout<< "copy " <<std::endl;
data = NULL;
init(*rhs.data);
}
ClassOne& operator = (const ClassOne& rhs){
std::cout<< "assign " <<std::endl;
int* p_old = rhs.data;
data = new int(*p_old);
//delete p_old; // I have to delete this line to make my code work
return *this;
}
void init(int data_param)
{
if (data) {
delete data;
}
data = new int(data_param);
}
private:
int* data;
};
int main(int argc, const char * argv[]) {
ClassOne c1(10);
ClassOne c2(c1); // call copy constructor
ClassOne c3;
c3 = c1; // call assignment function
return 0;
}
You are trying to delete the other object's data member, when you are meant to delete your own (this) object's current data member instead. What you probably want is this:
ClassOne& operator = (const ClassOne& rhs){
std::cout<< "assign " <<std::endl;
delete data; // delete your own old data
data = new int(*rhs.data); // clone the rhs's data
return *this;
}
Your code fail because you are double deleting something: you delete the pointer in the copy assignment and in the destructor. Deleting a pointer don't make it null, that's why it passes the if in your destructor. (By the way, you don't need to check the pointer to be null before deleting it, delete does the check anyway)
I would recommend copy assignment to not alter the rhs variable, as deleting the data pointer can easily lead to memory access. I would rather implement a move constructor and assignment to make this behaviour explicit. I would remove copy constructor and assignment and add these function:
ClassOne(const ClassOne&) = delete;
ClassOne& operator=(const ClassOne&) = delete;
ClassOne(ClassOne&& rhs) {
std::swap(data, rhs.data);
}
ClassOne& operator=(ClassOne&& rhs) {
std::swap(data, rhs.data);
}
That would require std::move to be call on assignment.
Alternatively, you can implement a non-stealing copy constructor. That would require that youto deep copy the data pointer (copying the content instead of the pointer)
ClassOne(const ClassOne& rhs) {
if (!data) {
data = new int;
}
*data = *(rhs.data);
}
ClassOne& operator=(const ClassOne& rhs) {
if (!data) {
data = new int;
}
*data = *(rhs.data);
}
Related
I want to build my own full Vector class in C++. I started like this:
#include <iostream>
#include <initializer_list>
#define Print(x)(std::cout<< x << std::endl)
// Vector Class //
template <typename T>
class Vector
{
// Attributes
int length = 0;
T* array;
public:
// Default constructor
Vector()
: length(0), array(nullptr)
{ }
// Copy constructor
template<typename U>
Vector(const Vector<U>& other)
: Vector(other.len())
{
Print("Use Copy Constructor");
// Coppying other data to new array
array = new T[other.len()];
for (auto i=0; i < other.len(); i++)
array[i] = other[i];
}
// Move constructor
Vector(Vector<T>&& other)
: length(other.len()), array(other.array)
{
Print("Use Move Constructor");
// Deleting other array
other.length = 0;
other.array = nullptr;
}
// Constructor (does not allow uniform initialisation)
Vector(int length)
: length(length), array(new T[length])
{ }
// Constructor (with initialiser list and delegate constructor)
Vector(std::initializer_list<T> list)
: Vector((int)list.size())
{
std::uninitialized_copy(list.begin(), list.end(), array);
}
// Destructor
~Vector()
{
length = 0;
delete[] array;
array = nullptr;
}
// Function Len that returns the length
int len() const
{
return length;
}
// Operator[](int i) and its const overload
auto& operator[](int i)
{
return array[i];
}
auto& operator[](int i) const
{
return array[i];
}
// Copy assignment operator
template<typename U>
Vector& operator=(const Vector<U>& other)
{
Print("Use Copy Operator");
if (this != (Vector*)& other) {
/*
This works the same way but does not solve the problem:
Vector<typename std::common_type<T,U>::type> temp(other);
temp.swap(*this);
*/
delete[] array;
length = other.len();
array = new T[other.len()];
for (auto i = 0; i < other.len(); i++)
array[i] = other[i];
}
return *this;
}
// Move assignment opertator
Vector& operator=(Vector<T>&& other)
{
Print("Use Move Operator");
if (this != (Vector*)&other){
/*
This works the same way but does not solve the problem:
Vector<T> temp(std::move(other)); // moves the array
temp.swap(*this);
*/
delete[] array;
length = other.len();
array = other.array;
other.len() = 0;
other.array = nullptr;
}
return *this;
}
};
But if I now try to use the copy assignment operator like this:
Vector<double> double_vector = {3.4677, 3.212, 2.2, 6.3};
Vector<double> a = double_vector;
I get the following error message:
error: use of deleted function 'constexpr Vector<double>::Vector(const Vector<double>&)'
I assume that the problem lies within the copy constructor and copy assignment operator, but I sadly can't find a solution. What I find strange is that, if I comment out the move constructor and move assignment operator, the code does seem to work. This lets me think that the compiler has difficulty knowing which constructor to use. But I could also be very wrong about this.
Hope I gave enough info for an answer/push in the right direction.
A template is never a copy constructor, that is a converting constructor.
And as you have defined a bunch of other constructors, the otherwise default copy constructor will be defined as deleted.
I have 2 codes with Assignment operator and copy constructor and same driver code. But both are giving different output.
Code 1:
//constructor, copy constructor, assignment operator and destructor
#include <iostream>
using namespace std;
class A
{
int a;
int *b;
public:
//constructor
A()
{
a=0;
b = new int;
}
//Copy constructor
A (const A& oldObj)
{
cout<<"copy ctor"<<endl;
a = oldObj.a;
b = new int;
*b = *(oldObj.b);
}
//destructor
~A()
{
delete b;
}
//Assignment operator overloading
void operator = (const A& obj)
{
cout<<"ass op"<<endl;
a = obj.a;
*b = *(obj.b);
}
};
int main()
{
A a1;
A a2 = a1; //copy constructor
A a3;
a3 = a1;//assignment operator
}
Code 1 : output
copy ctor
ass op
Code 2
// Simple C++ program to demonstrate use of copy-and-swap
// idiom by improving above code.
#include <iostream>
#include <cstring>
using namespace std;
class anyArrayClass
{
int size;
int *ptr;
public:
anyArrayClass(int s=0):size(s),ptr(size? new int[size]:nullptr)
{
//cout<<"default ctor"<<endl;
}
// Copy constructor
anyArrayClass(const anyArrayClass& obj):size(obj.size),
ptr(size? new int[size]:nullptr)
{
cout<<"copy ctor"<<endl;
memmove(ptr, obj.ptr, size*sizeof(int));
}
friend void swap(anyArrayClass& obj1, anyArrayClass& obj2)
{
cout<<"swap"<<endl;
std::swap(obj1.size, obj2.size);
std::swap(obj1.ptr, obj2.ptr);
}
// overloaded assignment operator
// argument passed by value. calls copy ctor
anyArrayClass& operator=(anyArrayClass obj)
{
cout<<"assignment"<<endl;
// calling friend function
swap(*this, obj);
return *this;
}
~anyArrayClass()
{
delete[] ptr;
}
};
int main()
{
anyArrayClass obj1;
anyArrayClass obj2 = obj1;//copy
anyArrayClass obj3;
obj3 =obj1;//assignment
}
Code 2 output:
copy ctor
copy ctor
assignment
swap
In code 2, last 3 lines of output is coming from assignment operator call (line no 55).
Why it is calling copy constructor?
In the 1st code, your assignment operator takes an A object by (const) reference, so a new A object does not need to be created during each assignment.
In the 2nd code, your assignment operator takes an anyArrayClass object by value, so a new anyArrayClass object has to be created during every assignment. And since anyArrayClass does not implement a move constructor, the copy constructor is used.
I'm trying to write a unique_ptr implementation. I'm struggling with writing a move constructor. Here are my problems:
When I mark the move constructor as default, my resource is deleted twice, when I move assign a pointer (auto foo2 = std::move(foo); below) - why?
When I'm trying to assign the underlying pointer in the move constructor like this *rhs = nullptr (see implementation below), the compiler says *rhs is an rvalue and that I cannot assign anything to it.
Finally, rhs.m_ptr = nullptr works. Why does it work, when *rhs = nullptr doesn't?
My code:
#include <iostream>
namespace my
{
template <class T>
class unique_ptr
{
public:
unique_ptr()
{
m_ptr = new T;
}
unique_ptr(const unique_ptr&) = delete;
// move constructor
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
{
m_ptr = *rhs;
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
~unique_ptr()
{
delete m_ptr;
}
T* operator->()
{
return m_ptr;
}
T* operator*()
{
return m_ptr;
}
unique_ptr& operator=(const unique_ptr&) = delete;
// no move assignment yet
private:
T* m_ptr;
};
} // namespace my
struct Foo
{
Foo()
{
std::cout << "Foo" << std::endl;
}
~Foo()
{
std::cout << "~Foo" << std::endl;
}
void printHello()
{
std::cout << "Hello" << std::endl;
}
};
int main()
{
my::unique_ptr<Foo> foo;
foo->printHello();
auto foo2 = std::move(foo);
return 0;
}
On a side note, apparently I can pass a unique_ptr without any template parameter to methods inside the unique_ptr class template. Does compiler just assume it's T?
Please discard any other implementation faults that don't relate to the described problems. It's work in progress.
1) The default move constructor doesn't know about the semantics of your class. So it moves the pointer rhs, but it will not reset the other pointer, which will get deleted as well in the other destructor.
2) *rhs calls operator* and returns a temporary/rvalue T*, a copy of the internal pointer, and is not consistent with the usual operator* which should return a T& or a const T&.
3) see 2. you are returning a temporary object.
So finally, what you should have:
unique_ptr(unique_ptr&& rhs) // = default deletes m_ptr twice
: m_ptr(rhs.m_ptr)
{
rhs.m_ptr = nullptr; // *rhs = nullptr doesn't work (*rhs is an rvalue)
}
T& operator*() {return *m_ptr;}
const T& operator*() const {return *m_ptr;}
And so on.
You're trying too hard. You don't have to go through the external interface. Just assign values:
m_ptr = rhs.m_ptr;
rhs.m_ptr = nullptr;
In addition, operator*() should return a T&, not a T*.
This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 6 years ago.
I have a class
class Foo
{
public:
char* ptr;
Foo() { ptr=new char [10]; }
~Foo() { delete [] ptr; }
};
I have learnt that returning an object of this class is not possible as the dynamically allocated pointer is delete 'ed and creates a dangling pointer in the calling function
So how can I return An object of this class??
Foo Bar ()
{
Foo obj;
return obj;
}
Can it be solved by adding a copy constructor
Foo::Foo(const Foo& obj)
{
ptr = new char [10];
for( int i = 0 ; i < 10 ;++i )
ptr [i] = obj.ptr[i];
}
And the function as
Foo Bar ()
{
Foo obj;
return Foo(obj); //invokes copy constructor
}
Note These are just representation of the actual class I want and is not created according to recommended standards (ie. Please don't tell me to use std::string or std::vector).
So how can I return An object of this class??
You need to implement the copy constructor and the copy assignment operator that manage memory properly.
// Copy constructor
Foo(Foo const& copy) : ptr(new char[strlen(copy.ptr)+1])
{
strcpy(ptr, copy.ptr);
}
// Copy assignment operator
Foo& operator=(Foo const& rhs)
{
// Don't do anything for self assignment
if ( this != &rhs )
{
delete [] ptr;
ptr = new char[strlen(rhs.ptr)+1]);
strcpy(ptr, rhs.ptr);
}
return *this;
}
If ptr is always going to be an array of 10 chars, you'll need to rethink the copy constructor and copy assignment.
// Copy constructor
Foo(Foo const& copy) : ptr(new char[10])
{
memcpy(ptr, copy.ptr, 10);
}
// Copy assignment operator
Foo& operator=(Foo const& rhs)
{
// Don't do anything for self assignment
if ( this != &rhs )
{
memcpy(ptr, rhs.ptr, 10);
}
return *this;
}
i try to use the std::map with
class DEMO {
public:
DEMO();
virtual ~DEMO();
DEMO &operator =(const DEMO &d);
DEMO(const DEMO& d);
BYTE* Arr() const;
private:
BYTE *m_array;
};
DEMO &DEMO::operator =(const DEMO &d) {
memcpy(m_array, d.Arr(), 1);
return *this;
}
DEMO::DEMO(const DEMO& d) {
//call operator=
*this = d;
}
const BYTE* DEMO::Arr() const {
return m_array;
}
DEMO::DEMO() {
m_array = new BYTE[1];
}
DEMO::~DEMO() {
if (m_array != 0)
delete [] m_array;
}
class MyClass {
private:
typedef map<unsigned int,DEMO> t_mapType;
t_mapType m_map;
void Test();
}
void MyClass::Test() {
DEMO myDEMO;
m_map[1] = myDEMO;
}
if i call Test() within the class, i get the error assert _CrtIsValidHeapPointer...
i checked with breakpoints and i see, during assingment (m_map[1] = myDEMO;) the destructor of DEMO gets called and i get the error on delete [] m_array; - how i make this running ?
The copy constructor and assignment operators are wrong. You need to allocate memory for m_array before doing memcpy. When you try to insert a DEMO object into the map using m_map[1] = myDEMO; a copy of the myDEMO is being created. To create a copy the DEMO class's copy ctor is used. Since the memory for m_array is not allocated while doing this copy, the memcpy call is crashing.
It's almost always wrong to base the copy ctor implementation on the assignment operator's. An assignment operator has to tear down the object's old state and build a new state. The copy ctor only has to build a new state.
If you want to do it this way, your copy ctor first needs to build a default state for the assignment operator to tear down. Of course, that's wasting resources.
That said, actually you should use the copy-and-swap idiom to base your assignment operator on your dtor and copy ctor instead. See here for a more thorough explanation of the above.
Use a std::vector to manage your BYTE*.
class DEMO {
public:
DEMO();
virtual ~DEMO();
//DEMO &operator =(const DEMO &d);
//DEMO(const DEMO& d);
const std::vector<BYTE>& Arr() const;
private:
std::vector<BYTE> m_array;
};
// Doesn't even need to be implemented by us anymore
//DEMO &DEMO::operator =(const DEMO &d) {
// memcpy(m_array, d.Arr(), 1);
// return *this;
//}
// Again, doesn't need to be implemented
//DEMO::DEMO(const DEMO& d) {
// //call operator=
// *this = d;
//}
// Just changed to return const vector&.
const std::vector<BYTE>& DEMO::Arr() const {
return m_array;
}
// Changed to use initializer list
DEMO::DEMO()
: m_array(1) {
// Old code: m_array = new BYTE[1];
}
// Doesn't need any code by us
DEMO::~DEMO() {
//if (m_array != 0)
// delete [] m_array;
}
i changed my code to use pointer to object instead of object for the value part in the map:
typedef map<unsigned int,DEMO*> t_mapType;
so i can assing as follow:
m_map[1] = new DEMO();
and i have to use a own free-mem method:
void DEMO::Clear() {
if (m_map.size() > 0) {
for (t_mapType::const_iterator it = m_map.begin(); it != m_mp.end(); ++it) {
if (it->second != 0)
delete it->second;
}
m_map.clear();
}
}