The class has two constructors, one that has no initialization value and does not increase
the 'val', the other takes a reference to an object, and increases 'val'.
How come, by initializing with 'A a,b = a;' a.get << b.get outputs '22' ?
I expected '12'.
Many thanks in advance.
class A
{
int *val;
public:
A() { val = new int; *val = 0; }
A(A &a) { val = new int; *val = a.get(); }
int get() { return ++(*val); }
~A () { delete val; }
};
main:
[...]
A a,b = a;
cout << a.get() << b.get(); // 22 ????
//cout << a.get(); // 1 if A a init, 2 if A a = a init, OK.
//cout << a.get() << b.get(); // 33 if A a = a, A b = a init.
[...]
Because in the A copy-constructor (which should really take its arguments as a reference to a const) you call a.get(). That increases *a.val from 0 to 1.
Then you call a.get() for the output, which increases *a.val from 1 to 2.
To get the expected behavior you should copy *a.val directly instead:
A(A const& a)
: val(new int(*a.val))
{}
a is default-constructed, so it sets *(a.val) to 0.
b is copy-constructed from a, so it first calls a.get(), incrementing *(a.val) to 1 and returning that new value, so *(b.val) gets set to 1, not 0.
Then you print the output of calling a.get() and b.get(), incrementing both *(a.val) and *(b.val) to 2 and returning those new values.
That is why you see 22 displayed.
Related
I have a struct A which has a member another struct C. So A with c1Ptr_ as member.
I use 2 structs S and N. N has as member A, a_ and S has as member C, c_.
After I instantiated A and create for S a c object using the created A object and I pass the created A object to N I would expect to have the A->c1Ptr_ in N as well.
Thank you.
#include <iostream>
#include <memory>
using namespace std;
struct C1
{
C1(int x):x_(x)
{
std::cout<<"-C1: x_: " << x_ << std::endl;
}
int x_;
~C1()
{
std::cout<<"-DC1: x_: " << x_ << std::endl;
}
};
using C1ptr = std::shared_ptr<C1>;
struct A
{
C1ptr c1Ptr;
};
struct S
{
S(C1ptr& c1Ptr):c1Ptr_(c1Ptr)
{
}
C1ptr c1Ptr_;
};
struct N
{
N(std::shared_ptr<A> a):a_(a)
{
}
std::shared_ptr<A> a_;
};
int main()
{
std::shared_ptr<A> a = std::make_shared<A>();
S s(a->c1Ptr);
N n(a);
s.c1Ptr_ = std::make_shared<C1>(12);
if (n.a_->c1Ptr)
{
std::cout<<"c1Ptr is set for N\n";
}
else
{
std::cout<<"c1Ptr is NOT set for N\n"; // why c1Ptr is not set for n.a_ ?
}
return 0;
}
shared_ptr doesn't share a pointer, it shares ownership of a pointer.
This means that you can change individual shared_ptr to point to other things, indepnendent of other shared_ptr's, but as soon as there's no shared_ptr pointing at something, that something get's deleted. Think of it as adding a counter to the pointer. When you create, delete or reassign a shared_ptr to something other than nullptr, you will increment or decrement the counter. If the counter reaches 0 (usually on a delete) then it also deletes the pointer.
In your case you are copying a nullptr from a. So you have two shared_ptr both storing nullptr. Then you change one of them to point to a new thing (12). This won't change anything else in memory, or specifically the value of other shared_ptrs.
You can try to work this out on your own by drawing the objects a, s, and n and their contents, and what their contents point to:
auto a = std::make_shared<A>(); // a(c1Ptr_ = null)
S s(a->c1Ptr_); // a(c1Ptr_ = null), s(c1Ptr_ = null)
N n(a); // a(c1Ptr_ = null), s(c1Ptr_ = null), n(a_ = a)
// [n.a_ points to a]
After this initial block of instructions:
a and s have their shared pointer members c1Ptr_ with a value of nullptr.
n has its shared pointer member a_ pointing to the object a.
s.c1Ptr_ = std::make_shared<C1>(12); // (1) a(c1Ptr_ = null), s(c1Ptr_->x_ = 12 ), n(a_ = a)
// [and you modify s]
if (n.a_->c1Ptr_) {
std::cout << "c1Ptr is set for N\n\n";
}
else {
std::cout << "c1Ptr is NOT set for N\n\n"; // (1)
}
Here:
you just modify s.c1Ptr_, but that doesn't affect a, and ultimately, n.
s.c1Ptr_ was originally set to point to the same C1 object as a.c1Ptr_ (actually, nullptr); now you're just making it point to something else.
a->c1Ptr_ = std::make_shared<C1>(15); // (2) a(c1Ptr_->x_ = 15 ), s(c1Ptr_->x_ = 12 ), n(a_ = a)
// [and you modify a]
if (n.a_->c1Ptr_) {
std::cout << "c1Ptr is set for N\n\n"; // (2)
}
else {
std::cout << "c1Ptr is NOT set for N\n\n";
}
What would have happened if you had changed a->c1Ptr_ instead? Since n->a_ points to a and we are modifying a, n->a_->c1Ptr_ is also set.
[Demo]
I've started to learn C++ but I have some doubts about the move semantics. When the book says that the move constructor of A "steals" the attributes of an rvalue B, is it means that the pointer to a value V switch from B to A? So in this way, A doesn't need to allocate another portion of memory?
If so, why in this code
class CBuffer{
private:
int size;
int csize;
char* ptr;
char rtp;
public:
CBuffer(int size): size(size), csize(0){
ptr = new char[size];
*ptr = 'a';
}
CBuffer(const CBuffer& source): size(3), csize(source.csize) {
this->ptr = new char[size];
memcpy(this->ptr, source.ptr, size);
}
CBuffer(CBuffer&& source){
this->ptr = source.ptr;
this->size = source.size;
std::cout << &this->ptr << std::endl;
std::cout << &source.ptr << std::endl;
source.ptr = nullptr;
}
};
int main(){
CBuffer a = CBuffer(1);
CBuffer b = std::move(a);
CBuffer c = b;
the lines
std::cout << &this->ptr << std::endl;
std::cout << &source.ptr << std::endl;
print different addresses?
The move semantics means that instead of creating a copy of another object, we allow the object to take any dynamically allocated memory inside of this other object.
We do so by swapping any dynamically allocated memory between the two, meaning, that the value (meaning, the memory address that is stored in the pointer) of the pointer in the first object will be now the value of the pointer in the new object, and we put a nullptr in the rvalue reference instead, so when it is destroyed, it will destroy it as well.
I have added a few things to the code:
I have initialized all of the variables in the initializer list. Also, I have removed the hard-coded initialization of size in the copy-ctor.
It is common to use the name other for the other object in copy/move operations.
An Error: you must put a value inside of the ptr, it is a bad practice not initializing your variables. Actually, it makes an invalid delete if we add a dtor, and use the swap idiom here (explained below).
Last but not least, the move ctor can be called on a live object, and instead of deleting the current ptr, you put nullptr on other, which means that is never deleted. for example, if main was:
int main()
{
CBuffer a{1};
CBuffer b{2};
a = std::move(b); // legal, but causes memory leak in your code, the value of a.ptr is never deleted!
}
How does the swap idiom helps us?!
Let's look at my example; When we swap the values (meaning a.ptr = old b.ptr and b.ptr = old a.ptr), whenever b is deleted, the "old" a.ptr, which now resides in b will be deleted, since we just swapped them. Usually, when we use the move semantics, the rvalue is going to be destroyed soon, and we just "ride" on its destruction in order to prevent memory leaks.
Your code should look like that (added some prints for better understanding):
class CBuffer{
private:
int size = 0;
int csize = 0;
char* ptr = nullptr; // very important!!!
char rtp = 0;
int id = 0;
public:
static int get_current_count()
{
static int object_cnt = 1;
return object_cnt++;
}
CBuffer(int size) :
size(size),
csize(0),
ptr(new char[size]),
rtp(0)
{
id = get_current_count();
std::cout << "On ctor of " << id << std::endl;
ptr[0] = 'a';
}
CBuffer(const CBuffer& other) :
size(other.size),
csize(other.csize),
ptr(new char[size]),
id(get_current_count())
{
std::cout << "On copy ctor of " << other.id << " to " << this->id << std::endl;
std::memcpy(this->ptr, other.ptr, size);
}
CBuffer(CBuffer&& other) :
size(other.size),
csize(other.csize),
rtp(other.rtp),
id(get_current_count())
{
std::cout << "On move ctor of " << other.id << " to " << this->id << std::endl;
std::swap(this->ptr, other.ptr);
}
~CBuffer()
{
std::cout << "On dtor of " << id << std::endl;
delete[] ptr;
}
};
int main()
{
CBuffer a = CBuffer(1);
CBuffer b = std::move(a);
CBuffer c = b;
}
And the output is:
On ctor of 1
On move ctor of 1 to 2
On copy ctor of 2 to 3
On dtor of 3
On dtor of 2
On dtor of 1
class Test
{
private:
int x;
int y;
public:
Test(int x = 0, int y = 0) { this->x = x; this->y = y; }
Test &setX(int a) { x = a; return *this; }
Test &setY(int b) { y = b; return *this; }
void print() { cout << "x = " << x << " y = " << y << endl; }
};
int main()
{
Test obj1(5, 5);
obj1.setX(10).setY(20);
obj1.print();
return 0;
}
The above code has output as 10 20 but when I change the return type of
Test &setX to Test setX and Test &setY to Test setY, the output changes to 10 5. Can anyone explain the reason for the same ? This is not an assignment quoestion or anything related to homework.
It is all about temporary object.
In the version without return reference, i.e. return type is Test
Your code is equivalent to
Test obj1(5, 5);
Test temp1 = obj1.setX(10);
Test temp2 = temp1.setY(20);
// temp2 will have x = 10 and y = 20, but the object is discarded
obj1.print();
as you can see setY(20) is called on an temporary object, and the returned value is discarded. So only the first setX(10) actually modify obj1
On the other hand, if you return a reference, i.e. the return type is Test &, no temporary will be created. So both setX(10) and setY(20) will affect the original object (obj1), because the method are called on a same object.
In the following line when you return a value (and not a reference) you end up having the obj1.setX(10) returning a new object. On this new object (that will be discarded) setY is called but the original remains unchanged
obj1.setX(10).setY(20);
when you change Test &setX(int a) into Test setX(int a), the function Test setX(int a) returns a new object. obj1.setX(10).setY(20) equal to the follows:
Test newobj = obj1.setX(10);
newobj.setY(20);
obj1.print(); // 10, 5
newobj.print(); // 10, 20
If you define
obj1.setX(10);
obj1.setY(20);
result always will be 10 and 20.
s(const s& src)
{
cout<<"Copy\n";
p =src.p;
}
void disp(int x=0)
{
*p = x;
cout <<"Value at p :"<<*p <<endl<< p<<endl;
}
};// relevant piece of code
s s1;
s s2 = s1;
s1.disp(200);
s2.disp();
In this program what i was expecting was since the data member p of objects s1 and s2
point to the same memory location any change in the value of p of object s1 should
reflect the value of p in object s2.In the output its clear that address holded by p
is same for both s1 and s2.but i didnt get the expected result 200 for s2.disp
function.Output is
Copy
Value at p :200
0x1e069010
Value at p :0
0x1e069010
You modify p in disp - *p = x;. When you call s2.disp(); the default 0 is used. (void disp(int x=0)), so you set *p to 0.
My approach was right,just make the variable p as public and print *(s2.p) right after calling s1.disp();.I can see the value of p for s2 displaying as 200
In your code, you modify p in the method dist :
*p = x;
In the second case, when you call s2.disp() the default value 0 is used, and *p has the value 0.
To reflect what you want, you can do :
class s
{
public:
s(const s& src)
{
cout<<"Copy\n";
p = src.p;
}
void Display() const // Display method just display
{
cout << "Value at p :" << *p << endl << p << endl;
}
void UpdateP( int x = 0 )
{
*p = x;
}
};
s s1;
s s2 = s1;
s1.UpdateP(200);
s1.Display();
s2.Display();
Just a live example, the code is not the prettiest but it shows you that it is working.
This is my sample program
#include "stdafx.h"
class B
{
public:
int i,j;
};
class A
{
public:
B b[2];
A()
{
b[0].i = 1;
b[0].j = 2;
b[1].i = 3;
b[1].j = 4;
}
B* function()
{
return b;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B* obj = new B();
obj = a.function();
return 0;
}
I have to get the array of b objects(ie, need all the values, b[0].i, b[0].j,b[1].i and b[1].j)
But when I tried it with this code, only one object is returned.
What you state in the question is not true. Two objects are indeed returned. Access them with obj[0] and obj[1].
I guess you are looking at obj under the debugger and the IDE cannot know that you mean for your pointer obj to be an array of two objects. So the tooltips will only show the first object, obj[0], or *obj. But the other object, obj[1] is definitely there.
Add the following line after the call to a.function:
printf("%d, %d, %d, %d\n", obj[0].i, obj[0].j, obj[1].i, obj[1].j);
and you will see this output:
1, 2, 3, 4
Note that there is no point in the line B* obj = new B(); since you immediately overwrite obj. You should do it this way:
B* obj = a.function();
Your code is also a little dangerous in that you must keep a alive at least as long as you are making references to obj.
This code
B* function()
{
return b;
}
returns the pointer to the first element of array
B b[2];
which can be dereferenced applying pointer arithmetics and/or operator [].
Yes, you are actually returning the a pointer to the array b[2]. What you want to do now is to iterate through the items in that pointer. You can print them by adding this lines to you code:
A a;
B *obj = a.function();
std::cout << obj[0].i << ", " << obj[0].j << "; " << obj[1].i << ", " << obj[1].j << std::endl;
And of course, including iostream at the beginning of your file:
#include <iostream>