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]
Related
I was reading about passing unique_ptr to a function here:
Can I pass a unique_ptr's reference to a function?
The winner answer tell that this function takes ownership:
void func1(unique_ptr<Clss>&& moved_obj) // this function takes ownership
{
//do something with the moved_obj
moved_obj.reset();
}
I did a small example to understand:
// The functions used for that
void pass_unique_by_copy(std::unique_ptr<int> T) { *T = 9; }
void pass_unique_by_ref(std::unique_ptr<int> &T) { *T = 9; }
void pass_unique_by_move(std::unique_ptr<int> &&T) { *T = 9; }
// main code
std::unique_ptr<int> g = std::make_unique<int>();
pass_unique_by_copy(std::move(g));
assert(g == nullptr);
cout << "The ownership as given\n";
// passed as reference is ok
g = std::make_unique<int>();
pass_unique_by_ref(g);
assert(g == nullptr);
cout << "Still the owner\n";
// moved ownership
pass_unique_by_move(std::move(g));
assert(g == nullptr); // --> g is not nullptr , why ?
cout << "content was moved lost ownership\n";
The output should be :
The ownership as given
Still the owner
content was moved lost ownership
But It's been held on the last assert
1- If I am moving the ownership to the function g should be nullptr after isn't ?
2 - why the guy put a reset inside the function ? I didn't get it could someone explain me ?
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.
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
I would like the function to return different types depending on different parameter values, but how can I print the variable the void pointer points to
in main()?
#include <iostream>
#include <string>
using namespace std;
void * func(int a)
{
if (a == 1)
{
int param = 5;
return ¶m;
}
else if (a == 2)
{
double param = 5.5;
return ¶m;
}
else if (a == 3)
{
string param = "hello";
return ¶m;
}
else
{
return nullptr;
}
}
int main()
{
void *ptr = func(3);//
cout << ptr;// print the address not the value
getchar();
return 0;
}
param is an automatic variable. You cannot return it and use it outside its scope.
param exists only within func, if you return it, the result is Undefined Behaviour.
To fix it you can either:
allocate param on the heap dynamically. After you do that, you can safely return param address but you have to remember to free it when you don't need it.
Here is correction of your code
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
void * func(int a)
{
if (a == 1)
{
int *param = new int(5);
return param;
}
else if (a == 2)
{
double *param = new double(5.5);
return param;
}
else if (a == 3)
{
char *param = new char[50];
strcpy(param, "test");
return param;
}
return nullptr;
}
int main()
{
int *ptr = (int*)func(1);
cout << *ptr << std::endl; // print the int value
delete ptr;
double *ptr2 = (double*)func(2);
cout << *ptr2 << std::endl; // print the double value
delete ptr2;
char *ptr3 = (char*)func(3);
cout << ptr3 << std::endl; // print the string
delete[] ptr3;
getchar();
return 0;
}
If you can use C++17, you can easily solve it by using a std::variant instead of a void *:
#include<iostream>
#include<string>
#include<variant>
std::variant<int, double, std::string, void *> func(int a) {
if (a == 1) {
int param = 5;
return param;
} else if (a == 2) {
double param = 5.5;
return param;
} else if (a == 3) {
std::string param = "hello";
return param;
} else {
return nullptr;
}
}
int main() {
std::visit([](auto v) {
std::cout << v << std::endl;
}, func(3));
}
See it up and running on wandbox.
In C++11/14 you can do the same with a tagged union. The basic idea is that what you return contains enough information so that the caller can get out of it the original type.
Alternatives exist.
As an example, you could erase the type and return a pair that contains both the original (erased) variable and a pointer to function filled with an instantiation of a function template. The latter will be able to reconstruct the original variable from a void * for it knows its type.
Well, pretty much a great machinery you can avoid to use with a tagged union or a std::variant (more or less a type-safe version of a tagged union at the end of the day).
What you're returning is the address of a local variable. That variable goes out of scope when the function returns, meaning that the memory it was using could be reused. Attempting to dereference that pointer (i.e. access the memory it points to) invokes undefined behavior.
Even if you were returning a valid pointer, the fact that your function returns a void * means that any type information regarding what that pointer was pointing to is lost. You could print one or more bytes starting at that address, but it won't tell you what the original type was.
Even if that pointer were valid, you simply can't have enough information to force safely a cast to something and then print it.
No information of its size, no information of its internal layout. So,you simply can not print what's pointed by a void*, unless you have some information prepared by hand somewhere, and force a static_cast to the known type.
For example:
double x = 1.2;
int y = 5;
int f(void** output) {
static int x;
if ( x++ ) {
*output = &x;
return 1;
}
*output = &y;
return 2;
}
...
void* out;
int r = f(&out);
if ( r == 1 ) cout << *(static_cast<double*>(out));
else if ( r == 2 ) cout << *(static_cast<int*>(out));
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>