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;
}
Related
I've looked around and only found a solution for either problem, which conflicts with the other.
I have
class A with some const member and other members that need to be initialized
class B with some constructor and a 2D array of A's
like this:
class A {
public:
A();
A(int t, int u);
const int x = 5;
private:
int _t;
int _u;
}
class B {
public:
B();
A a[50][500];
}
A::A() {
//nothing here
}
A::A(int t, int u) {
_t = t;
_u = u;
}
B::B() {
for (int i=0; i<50; i++)
for(int j=0; j<500; j++)
a[i][j] = A(i, j);
}
Now, this doesn't work because A has a const member, so the assignment fails because all the a's have already been default constructed, I obviously don't want to code 25000 initializer values in the member initializer list of B so that is out of the question.
I need the default initializer because otherwise the constructor of B will throw an error that i can't default initialize the array so i need some way to reassign the array with a newly constructed object of A.
How could I accomplish this?
This is what you want?
class A {
public:
A();
A(int t, int u);
const int x = 5;
A& operator=(const A& a);
private:
int _t;
int _u;
};
class B {
public:
B();
A a[50][500];
};
A::A() {
//nothing here
}
A& A::operator=(const A& a) {
_t = a._t;
_u = a._u;
}
A::A(int t, int u) {
_t = t;
_u = u;
}
B::B() {
for (int i=0; i<50; i++)
for(int j=0; j<500; j++)
a[i][j] = A(i, j);
}
You are just missing the = operator
If you want to avoid default-initializing your array, you can use std::vector<std::vector<A>> and populate it in B's constructor :
class A {
public:
A(t, u);
const int x = 5;
private:
int _t;
int _u;
}
class B {
public:
B();
std::vector<std::vector<A>> a;
}
A::A(t, u) {
_t = t;
_u = u;
}
B::B()
: a(50) { // a will contain 50 empty rows
for (int i=0; i<50; i++) {
a[i].reserve(500); // allocates enough space in each row for at least 500 elements
for(int j=0; j<500; j++)
a[i].emplace_back(i, j); // constructs value in-place using given arguments
}
}
Note that you don't need A's empty-parameter constructor anymore, so I have removed it.
I am learning dynamic memory allocation. I have the following class where 'class A' should own a dynamically allocated array in the constructor. Also the copy constructor and destructor should be modified. This is what I have so far...
#include <iostream>
#ifndef A_HH
#define A_HH
#include "B.hh"
class A {
public:
A() { B *array = new B[12];}
A(const A&) { /* Do not know what to put here..*/ }
~A() { delete[] array;}
private:
//B array[12] ; <- This is the array that I have to modify so it becomes dynamic.
B *array;
} ;
#endif
For starters your default constructor there is overriding the member variable "array" with a local variable of the same type, so you want the default constructor to look like this:
A() { array = new B[12]; }
Then the copy constructor presumably needs to deep copy the array over, but with a simple array you can't tell the arraysize at runtime. You need to either move to a smarter container (e.g. stl::vector) or store the size, but a naive copy constructor would look like this:
A(const A& other)
{
array = new B[12];
for(int i=0;i<12;i++)
{
array[i] = other.array[i];
}
}
Check out this code:
#include <iostream>
using namespace std;
class MyArray{
public:
int* array;
int length;
MyArray(int length){ // constructor which takes length of array as argument
this->length = length;
this->array = new int[this->length];
for(int i=0; i<this->length; i++){
this->array[i] = i;
}
}
MyArray(const MyArray &obj){
this->length = obj.length;
this->array = new int[this->length];
for(int i=0; i<this->length; i++)
{
this->array[i] = obj.array[i];
}
}
~MyArray(){ // destructor
delete[] this->array;
}
void print(){ // test method for printing the array
for(int i=0; i<this->length; i++){
cout << this->array[i] << endl;
}
cout << endl;
}
private:
protected:
};
int main()
{
MyArray *array = new MyArray(10);
MyArray *array2 = new MyArray(*array); // call copy constructor
array->print();
array2->print();
delete array;
delete array2;
}
I was testing a move constructor and did the following:
#include <iostream>
#include <string>
using namespace std;
class X{
public:
int* p;
int size;
X(){}
X(int n) : size(n){
p = new int[n];
for(int i = 0; i < n; i++)
p[i] = i;
cout << "Constructor\n";
}
~X(){
delete[] p;
}
X(const X& r){
cout << "Copy\n";
}
X(X&& r){
p = r.p;
size = r.size;
r.p = NULL;
r.size = 0;
cout << "Move\n";
}
};
int main() {
X a(10); //constructor
X b(a); // copy
X c(X(3)); //constructor, move
return 0;
}
What I expected in the output is in the comments, but when compiling (VS 2012) move constructor is not called?! But, if I add additional parameter to constructor:
string name;
X(int n, string _name) : size(n), name(_name){
p = new int[n];
for(int i = 0; i < n; i++)
p[i] = i;
cout << "Constructor\n";
}
and then
X a(10, "a"); //constructor
X b(a); // copy
X c(X(3, "pom")); //constructor, move
i get results as expected... I really don't understand why.
EDIT: Now tested on GCC 4.7.2 and it does not call Move constructor in both cases but C++ Builder XE5 compiler calls Move constructor in both cases. Yet, VS calls it only in the second case (when using additional constructor parameter). Very interesting...
The compiler is eliding the move construction and directly constructing X(3) into c, essentially turning your initialization into
X c(3);
With gcc, you can disable this using the -fno-elide-constructors switch. Once you add that, the output from your original example is as expected:
Constructor
Copy
Constructor
Move
Live demo
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])
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.