I have a question regarding how the destructor is called. For example, I created the following foo class and supplied with copy constructor, destructor and overloaded the assignment operator. I created a dynamic array of this foo objects and used the operator "=" to assign individual element of the array. I am very confused, immediately after the assignment operation, the destructor is called and when I want to access the data in the newly assigned object, I got very confusing result.Any suggestions?
#include <iostream>
using namespace std;
bool debug = true;
class foo{
private:
int n;
void init(int _n);
public:
int* arr; // just to make it accessible so that we can tract the contents;
foo(int _n);
foo(int* _a, int len);
foo(const foo & rhs);
foo & operator=(const foo & rhs);
~foo();
};
void foo::init(int _n = 0){
n = _n;
arr = new int[n];
for(int i = 0; i != n; i++) arr[i] = 0;
}
foo::foo(int _n = 0){
init(_n);
}
foo::foo(int*_a, int len){
init(len);
for(int i = 0; i< len; i++) arr[i] = _a[i];
}
foo::foo(const foo &rhs){
operator = (rhs);
}
foo& foo::operator= (const foo &rhs){
if(debug) cout<<"\nassignment operator overloaded";
if (this != &rhs){
if(n != 0) {
n = rhs.n;
delete [] arr;
arr = new int[n];
for(int i = 0; i < n; i++) arr[i] = rhs.arr[i];
}
}
return *this;
}
foo::~foo(){
if (debug)cout << "\ndestructor called\n";
delete []arr;
}
int main(){
{ // a explicit block to see when the destructor is called;
foo* f = new foo[4];
int n = 4;
int a[] = {0,1,2,3};
for(int i = 0; i < n;i++) {
cout<<i;
f[i] = foo(a, i);
cout<<f[i].arr[i]<<"\n"; // result is some seemingly random number;
}
}
system("PAUSE");
}*
When you do this:
f[i] = foo(a, i);
a temporary foo object is created on the RHS of the assignment operator. It is then used to assign to the foo on the LHS of the operator. Then, it is destroyed, therefore its destructor is called.
The reason for the garbage values after the assignments is probably that n is 0 in all the foos in the array. Your assignment operator is broken. You may want to have a look at the copy and swap idiom.
A big no no! Delegating initialization of an uninitialized object to the assignment operator is a no good idea:
foo::foo(const foo &rhs){
operator = (rhs);
}
Better is:
foo::foo(const foo &rhs)
: n(rhs.n), arr(new int[n])
{
// copy rhs.arr to arr
}
// Note: Passing by value:
foo& operator = (foo rhs) {
std::swap(n, rhs.n);
std::swap(arr, rhs.arr);
return *this;
// Note: The swapped rhs will do the cleanup in the destructor
}
You will end up with less coding and exception safety
Another issue is:
cout << f[i].arr[i] << "\n"
You are printing an undefined 'end' value (arr[i] == arr[n])
Related
This question already has an answer here:
C++ Deep copy of dynamic array through assignment operator
(1 answer)
Closed last year.
I try to learn copy constructors principle.
I have this code and I need to COPY data to testB data type.
Right now, I only copy pointers to previous memory but I need to copy all data.
Can you help me?
#include <cstdio>
#include <stdio.h>
struct Test {
int i, *p;
Test(int i): i(i), p(new int[i]){
p[0] = 1;
p[1] = 2;
p[2] = 3;
}
~Test() {
}
};
struct TestB : Test {
TestB(const Test &test) : Test(test){
p = new int[3];
p[0] = test.p[0];
p[1] = test.p[1];
p[2] = test.p[2];
i = test.i;
}
};
int main(){
Test test(3);
TestB b = test;
return 0;
}
First off, what you are trying to implement is a converting constructor, not a copy constructor, since you are trying to convert a Test object into a TestB object, rather than create a copy of a TestB object from another TestB object.
Since the array exists in Test, you should implement an actual copy constructor (and a copy assignment operator) in Test, and then TestB's converting constructor can call Test's copy constructor. Test's copy constructor can then allocate a new array that has the same number of items as the array being copied, and copy the values from the original array into the new array, eg:
#include <utility>
struct Test {
int i, *p;
Test(int i): i(i), p(new int[i]) {
for(int j = 0; j < i; ++j)
p[j] = j+1;
}
Test(const Test &src): i(src.i), p(new int[src.i]) {
for(int j = 0; j < i; ++j)
p[j] = src.p[j];
}
~Test() {
delete[] p;
}
// don't forget an assignment operator too, per the Rule of 3:
// https://en.cppreference.com/w/cpp/language/rule_of_three
Test& operator=(const Test &rhs) {
if (this != &rhs) {
Test tmp(rhs);
std::swap(i, tmp.i);
std::swap(p, tmp.p);
}
return *this;
}
};
struct TestB : Test {
TestB(const Test &test) : Test(test) {}
};
int main(){
Test test(3);
TestB b = test;
return 0;
}
That being said, you should use std::vector instead of new[], then you get all of the array management for free since std::vector already implements everything for you, eg:
#include <vector>
struct Test {
std::vector<int> p;
Test(int i): p(i) {
for(int j = 0; j < i; ++j)
p[j] = j+1;
}
};
struct TestB : Test {
TestB(const Test &test) : Test(test) {}
};
int main(){
Test test(3);
TestB b = test;
return 0;
}
I have written following class which has overloaded assignment operator. As shown in example everywhere I returned *this from assignment operator.
class Sample
{
int *p;
int q;
public:
Sample()
{
cout<<"Constructor called"<<endl;
p = new int;
q = 0;
}
Sample& Sample::operator =(const Sample &rhs)
{
cout<<"Assignment Operator"<<endl;
if(this != &rhs)
{
delete p;
p = new int;
*p = *(rhs.p);
}
return *this;
}
void display()
{
cout<<"p = "<<p<<" q = "<<q<<endl;
}
};
When I call assignment operator like a = b, it goes like, a.operator=(b);.
Now I am calling a's operator function, this is already being passed with operator = then why it is required to return it from assignment operator?
You have to return *this (and also by reference) if you want to support chaining of assignment. For e.g
Class A
{
};
A x,y,z,w;
x = y = z = w; //For this you are returning *this.
EDITED FOR MORE CLARIFICATION:- ( In response to your comment )
Let's suppose you're not returning anything from your assignment operator then expression will be evaluated as follows:-
x=y=z => x=(y=z)
Above will result into a call to
y.operator(z)
as assignment operator is right associative.
After this next call would be to
x.operator ( value returned from y=z) ).
If you don't return any value chaining would fail.
Hope I am clear
You're returning it to allow for chaining, you can't chain an assignment sequence without returning a reference to Sample, perhaps this will make it more clear:
int count = 0;
class Sample {
int *p;
int q;
int m_count;
public:
Sample() {
m_count = count;
cout<<"Constructor called for m_count = "<< count++ << endl;
p = new int;
q = 0;
}
Sample& operator =(const Sample &rhs) {
cout<<"Assignment Operator (m_count " <<
m_count << " = m_count " << rhs.m_count << ") " <<endl;
if(this != &rhs)
{
delete p; // Unnecessary
p = new int; // Unnecessary
*p = *(rhs.p);
}
return *this;
}
};
int main() {
Sample a;
Sample b;
Sample c;
// [b = c] will return a Sample& to the "changed" b
a = b = c;
}
Example
Counter-example with void return value:
int count = 0;
class Sample {
int *p;
int q;
int m_count;
public:
Sample() {
m_count = count;
cout<<"Constructor called for m_count = "<< count++ << endl;
p = new int;
q = 0;
}
void operator =(const Sample &rhs) {
cout<<"Assignment Operator (m_count " <<
m_count << " = m_count " << rhs.m_count << ") " <<endl;
if(this != &rhs) {
delete p; // Unnecessary
p = new int; // Unnecessary
*p = *(rhs.p);
}
}
};
int main() {
Sample a;
Sample b;
Sample c;
a = b; // Valid
a = b = c; // Not valid - error: no viable overloaded '='
}
An assignment statement,
a = b;
Requires that b should an R-value, and a must be an L-value. When you change the assignment as:
a=foo();
The expression foo(), that is the function call to foo must result in an R-value. If foo returns void, it doesn't produce R-value (or any value). Therefore, foo is required to return a value (Via a explicit return statement(s)). That's the language mandate!
The b, in first statement may be L-value also. For example:
a = b = c;
The variable b is both L-value (for b=c), as well as R-value (for a=b). When operator= returns T&, it may act as L-value (as well as R-value). But when operator= returns const T&, it may only be R-value. Therefore, if a class returns a const reference, following wont work:
a = b = c;
Here, c is assigned to a (in the overload itself), but b is a const (R-value only). It won't allow a=b.
Its mainly to support assignment chaining.
Sample A,B,C;
A=B=C;
You can have a look into the existing questions:
Why should the assignment operator return a reference to the object?
Why must the copy assignment operator return a reference/const reference?
I would like to further exhaust this topic.
Assume that I have something like:
class MyClass
{
public:
MyClass(int N)
{
data_ptr = new float[N];
};
float* dat_ptr;
// ... clever operator definition here ...
};
So I would like to be able to simply write:
MyClass a(4);
MyClass b(4);
MyClass c(4);
// modify b.data_ptr and c.data_ptr ....
// Use "clever operator"
a = b + c;
Where the operator would do a.data_ptr[i] = b.data_ptr[i] + c.data_ptr[i] for i=0:(N-1) ...
Hence no extra copies of the data are created and we are neatly using the preallocated buffers.
Is this possible? If so, please provide me with som insights as to how it would be done.
Thanks!
You can, if you use move semantics from C++11.
class MyClass
{
public:
MyClass(int N)
{
data_ptr = new float[N];
n = N;
}
MyClass(MyClass && rhs)
{
data_ptr = rhs.data_ptr;
n = rhs.n;
rhs.data_ptr = nullptr;
}
// dtor, copy-ctor etc.
int n;
float * dat_ptr;
};
MyClass operator + (const MyClass & left, const MyClass & right)
{
MyClass result(left.n);
// Implement addition
}
// Note: no error-checking
This way a temporary object will be created, but the internal data will not be unnecessarily copied.
Read more about the move semantics.
It is not possible; Before a is assigned to, a temporary object will be created as a result of calling operator + (b, c); This operator should return the created instance, that should then be assigned to a; the created instance is always created by b + c.
What is possible though is to define += as a member operator and say:
b += c;
This would modify the value of b without creating extra copies.
Edit: I have reconsidered :)
You definitely can do it, by abstracting operations as lazy evaluation objects.
Here is an example:
class MyClass; // fwd. declaration of your class
struct LazySum
{
LazySum(const MyClass& a, const MyClass& b)
: x(a), y(b) {}
float operator[](int i) { return x[i] + y[i]; }
const MyClass& x;
const MyClass& y;
};
class MyClass
{
public:
MyClass(int N)
{
data_ptr = new float[n = N];
};
int n; // this shouldn't be public
float* dat_ptr; // nor this, but I went with your code
// ... clever operator definition here ...
MyClass& operator=(const LazySum& terms)
{
// ignore case when n != x.n or n != y.n
// because not the point of the example
// (and I'm lazy)
// sum evaluation occurs here
// with no new allocations
for(int i = 0; i < n; ++i)
data_ptr[i] = terms[i];
return *this;
}
};
LazySum operator=(const MyClass& x, const MyClass& y)
{
return LazySum(x, y); // LazySum is a couple of references in size
}
void client_code_using_clever_op()
{
MyClass a(4);
MyClass b(4);
MyClass c(4);
// modify b.data_ptr and c.data_ptr ....
// Use "clever operator"
a = b + c; // actual sum performed when operator = is executed
}
The idea is to store the terms, and perform late evaluation on the terms.
Points of improvement:
inject a functor in the construction of LazySum to make it become LazyOp (the functor would decide what the op is); Implement other binary operators on MyClass in terms of it.
use RAII in MyClass.
when you need to implement lazy evaluation operators on another type (e.g. some MyOtherClass) consider implementing LazyOp as a template on the terms and functor type.
this does not support more complex expressions without some extra work:
MyClass a(4), b(4), c(4), d(4);
d = (a + b) + c; // error
This example will not work because it would require an operator+(const LazySum&, const MyClass&);;
As Spook explained, yes it is possible. Just for fun I wrote a full example that you can compile and run. If a copy was to be created, you would get a message in the output. I tried this example in Visual Studio 2012 and runs fine.
class MyClass
{
private:
float *data_ptr;
std::size_t size;
public:
MyClass(std::size_t N = 0) :
size(N),
data_ptr(N ? new float[N]() : nullptr)
{}
MyClass(const MyClass& other) :
size(other.size),
data_ptr(other.size ? new float[other.size]() : nullptr)
{
std::copy(other.data_ptr, other.data_ptr + size, data_ptr);
std::cout << "Copy!" << std::endl;
}
MyClass(MyClass&& other)
{
size = 0;
data_ptr = nullptr;
swap(*this, other);
}
~MyClass()
{
delete[] data_ptr;
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
friend MyClass operator+(MyClass& first, MyClass& second)
{
MyClass result(std::min(first.size, second.size));
for (std::size_t i=0; i < result.size; i++) {
result.data_ptr[i] = first.data_ptr[i] + second.data_ptr[i];
}
return result;
}
friend void swap(MyClass& first, MyClass& second)
{
std::swap(first.size, second.size);
std::swap(first.data_ptr, second.data_ptr);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
MyClass a(5);
MyClass b(5);
MyClass c(5);
a = b + c; //this should not produce an extra copy
return 0;
}
I'm trying to implement a container that allocated memory to the heap, but it seems as though my base constructor and my argument constructor don't like each other. Below, I've posted the code without anything commented out. As it stands, it crashes.
#include <iostream>
using namespace std;
class foo
{
public:
foo() {size=1; vals = new double[1]; vals[0]=0;}
~foo() {delete[] vals;}
foo(const foo& other)
{
size=other.getsize();
delete[] vals;
vals = new double[size];
for(long unsigned i=0; i<size; i++)
vals[i]=other[i];
}
foo& operator=(const foo& other)
{
size=other.getsize();
delete[] vals;
vals = new double[size];
for(long unsigned i=0; i<size; i++)
vals[i]=other[i];
return *this;
}
foo(double* invals, long unsigned insize)
{
size=insize;
delete[] vals;
vals = new double[size];
for(long unsigned i=0; i<size; i++)
vals[i]=invals[i];
}
double operator[](long unsigned i) const {return vals[i];}
long unsigned getsize() const {return size;}
private:
double* vals;
long unsigned size;
};
int main()
{
double bar[3] = {5,2,8};
foo B(bar, 3);
cout<< B[0]<< " "<< B[1]<< " "<< B[2]<<endl; //couts fine
foo A; //crashes here
return 0;
}
However, when I change main to be:
int main()
{
double bar[3] = {5,2,8};
foo B(bar, 3);
cout<< B[0]<< " "<< B[1]<< " "<< B[2]<<endl; //couts fine
foo A(); //works now
return 0;
}
It runs fine. But then I can't assign A = B because it thinks foo is a function or something.
I assume you have some really compelling reason not to use std::vector<double> here...
But anyway... in your copy constructor, you don't want to delete[] vals.
foo(const foo& other)
{
size=other.getsize();
vals = new double[size];
for(long unsigned i=0; i<size; i++)
vals[i]=other[i];
}
When the copy constructor is called, your object hasn't been initialized yet, so vals* doesn't even point to anything valid. Therefore, deleting it invokes undefined behavior (and your program crashes.) You only need to delete[] vals in your assignment operator.
Also, when you declare the Foo variable A, you don't want those parentheses after the variable name. Just say:
foo A;
When you place those parenthesis after the variable name, you're actually writing a function declaration using syntax inherited from C, and A becomes a function pointer type.
I have a following class foo
class foo
{
int *arr; // arr holds numbers
int sz; // size of array
public:
// Suppose I have made default and 1 parameter c'tor
foo(const foo &f)
{
sz = f.sz;
arr = new int[sz];
for(int i=0;i<sz;i++)
arr[i]=f.arr[i];
}
};
int main()
{
foo x(5); //5 is size of array
const foo y = x; //doesn't work as I haven't initialized in member-initialization list, but how to write for loop in member initialization list ?
}
So How do I write for loop in member initialization list ?
You could just use a std::vector in this caseā¦ anyways.
Typically, I will create a private static method which will perform the allocation and copy. Then the initialization list may be used:
static int* CloneInts(const foo& f) {
int* ints = new ...
...copy them from #a f.arr...
return ints;
}
Then your init-list would look like:
foo(const foo& f) : arr(CloneInts(f)), sz(f.sz) {
Have you tried constructing it with the copy constructor directly?
const foo y(x);
You should clarify your problem, because the one in the question doesn't actually exist.
The const foo y = x; line will compile and work with that copy constructor. A const object under construction isn't "const' until the constructor has completed. So the constructor body is permitted to modify the object even if the object being constructed is const.
Also note that the loop in the example isn't even modifying anything that's ever const - since the array is allocated dynamically, those array elements are modifiable even if the object itself isn't. For example, the arr pointer isn't modifiable after the ctor has completed, but arr[0] still is.
Try out the following to see both points in action:
#include <stdio.h>
#include <algorithm>
class foo
{
int *arr; // arr holds numbers
int sz; // size of array
public:
foo() : arr(0), sz(0) { puts("default ctor");}
foo(int x) : arr(0), sz(x) {
puts( "int ctor");
arr = new int[sz];
for(int i=0;i<sz;i++)
arr[i]=0;
}
foo(const foo &f)
{
puts("copy ctor");
sz = f.sz;
arr = new int[sz];
for(int i=0;i<sz;i++)
arr[i]=f.arr[i];
}
~foo() {
delete [] arr;
}
foo& operator=(const foo& rhs) {
if (this != &rhs) {
foo tmp(rhs);
std::swap( arr, tmp.arr);
std::swap( sz, tmp.sz);
}
return *this;
}
void update() const {
for(int i = 0; i < sz; i++) {
arr[i] = arr[i] + 1;
}
}
void dump() const {
for(int i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
puts("");
}
};
int main()
{
foo x(5); //5 is size of array
const foo y = x;
y.dump();
y.update(); // can still modify the int array, even though `y` is const
y.dump();
}
I think you may be confusing constructing const objects with constructing objects that have const members since those members must be initialized in the initialization list.