Slicing on shared pointer reassignment - c++

Is it always safe to reassign a std::shared_ptr<A> to point to values of type B, where B is a subclass of A? Specifically, in the following code:
class A {};
class B : public A {
int arr[1000];
};
int main() {
std::shared_ptr<A> pa = std::make_shared<A>();
*pa = B();
}
B has a much larger size than A, so if the shared pointer were to have only allocated enough memory for objects of type A, we would be overflowing. Is this properly handled by the library? I have been experiencing issues in a larger project with similar code, and they went away when I added a layer of indirection (shared_ptr<shared_ptr<A>>)

You are slicing your B object down to an A in that assignment. The assignment in your example is no different than if shared_ptr wasn't involved at all:
int main() {
A a;
a = B();
}
The only difference is that you're assigning to the object pointed to by pa rather than a local object.
If you actually want to make your shared_ptr point to a new B object, you need to allocate one and assign to the pointer rather than the object pointed to:
int main() {
std::shared_ptr<A> pa = std::make_shared<A>();
pa = std::make_shared<B>();
}
Or if you don't need the A object in the first place:
int main() {
std::shared_ptr<A> pa = std::make_shared<B>();
}

Related

Smart pointers vs dumb pointers: polymorphism behavior oddity

I was debugging an issue in some larger code and realized something strange about smart pointers and their polymorphic properties. This is best seen through the simple example:
#include <iostream>
#include <memory>
using namespace std;
class A {
public:
virtual void who() {cout << "I am class A" << endl; };
};
class B : public A{
public:
void who() {cout << "I am class B" << endl; };
};
int main(int argc, char *argv[])
{
B b;
A * aptr = &b;
aptr->who(); //Output: I am class B
B * bptr = &b;
bptr->who(); //Output: I am class B
shared_ptr<A> sptr;
sptr = make_shared<A>(b);
sptr->who(); //Output: I am class A
sptr = make_shared<B>(b);
sptr->who(); //Output: I am class B
return 0;
}
The first two outputs makes sense to me, but why can I access the member function defined in A (see the third output) when the only object I have initialized is of type B? In some sense, this is a nice trick for accessing members of the base class of an object of derived type. However, this still feels a bit spooky to me...
Can anyone explain why this behavior is possible with smart pointers but not regular pointers?
std::make_shared always creates a brand new object. That is,
sptr = make_shared<A>(b);
is more like
A* p1 = new A(b);
and not like
A* p2 = &b;
p1 and the return value of make_shared do not point at b at all.
The following statement:
sptr = make_shared<A>(b);
is basically the same as:
sptr = shared_ptr(new A(b));
Instead of creating a shared_ptr from a pointer to B, you are creating a new A object with the copy constructor of A.
make_shared<A>(b) uses b to construct an object of type A, and points to that newly constructed object. This slices the B object, so the shared_ptr truly points to an A.

Member function pointer point to method of another object

Is it possible to have a member function pointer of an object point to a method of another object? If so, please give an example.
Is it possible to have a member function pointer of an object point to a method of another object?
Yes.
If so, please give an example.
I'll give you two.
struct A {
void f() {}
};
struct B {
void (A::*p)();
};
int main () {
A a;
B b;
b.p = &A::f;
(a.*b.p)();
A* pA = &a;
B* pB = &b;
(pA->*pB->p)();
}

Pointing a derived smart pointer to a stack object returned by base reference

I have a problem. I'd like to point shared_ptrs to objects that are stored in a class. In the code there is a Holder::getSomething() function that returns a reference to a base. I'd like to cast that to the derived b_ptr. Here's the code:
#include <memory>
using namespace std;
class A{
public:
int a;
A() : a(0){}
virtual ~A(){}
};
class B : public A {
public:
bool b;
B() : A(){ b = false; }
};
class Holder{
public:
B arr[1];
// there's an A ref here, not B, because i'll have a boatload of deriveds.
A& getSomething(){
return arr[0];
}
Holder(){
arr[0] = B();
}
};
int main(){
Holder h;
shared_ptr<B> b_ptr;
// b_ptr = something_alien_here(h.getSomething());
return 0;
};
I know ( and by "know" i mean i have an uneducated guess ) that i should use dynamic_(pointer_?)cast but i cant find/figure out the right syntax.
The whole point of a shared pointer is that its ref counted, and destructs
what it points to when the last one go out of scope. You don't want that to happen
to a member object of another class, since that is undefined behaviour.
in short; don't do that.
If you can guarantee that h will live longer than b_ptr, then you can use the borrowing constructor of shared_ptr, together with a cast:
Holder h;
std::shared_ptr<B> b_ptr(std::shared_ptr<B>(),
&static_cast<B&>(h.getSomething()));
Now b_ptr shares ownership with the temporary, empty shared pointer, which has the effect of never calling the deleter for B. This is why it is now your responsibility to guarantee that the pointee exists for at least as long as the shared pointer may be dereferenced.

How to Initialize Member Variables

Probably an easy question for someone out there, but what am I doing wrong in the below example? I'm trying to build a global class which contains instantiations of other classes within... I think where I'm going wrong boils down to the below example. Getting a seg fault, as if *b is never created. Thanks in advance!!
#include <iostream>
using namespace std;
class A;
class B;
class B
{
public:
B()
{
b = 99;
}
~B();
int Getb() {return b; }
void Setb (int x) { b = x; }
private:
int b;
};
class A
{
public:
A()
{
B *b = new B;
}
~A();
B * b;
void Printout()
{
cout<<b->Getb()<<endl;
}
private:
};
int main()
{
A *a = new A;
a->Printout();
cin.get();
}
A() {
B *b = new B;
}
B * b;
In the constructor you're declaring a new local variable that gets assigned the address of the freshly allocated B, and then forgotten!
The instance field b is never assigned to because it is shadowed by the local variable of the same name in the constructor.
You probably mean to do
A() {
b = new B;
}
A()
{
B *b = new B;
}
should be
A()
{
b = new B;
}
In your version there a variable called b in the A constructor. This variable hides the A class member also called b (which was obviously the one you wanted to use).
In the cosntructor A::A() you don't initilize the A::b member, but a local variable instead. Try doing:
A() {
b = new B;
}
or better:
A():b(new B) {}
And even better, don't use the raw pointer at all.
B *b = new B;
Creates a local variable named b which shadows the class member b. You need to initialize the class member, and you should do it in an initialization list.
A() : b(new B) {}
Your next step is to fix the memory leak caused by never calling delete on the pointers you dynamically allocate, but since this is a learning exercise it's probably not terribly important (yet).
Although quite a few people have pointed out one way of fixing the problem you're seeing, none seems (to me, anyway) to be giving advice about how to really make the code better.
Your definition of B is what's called a quasi-class. To make a long story short, your B can be simplified a lot without losing anything:
struct B {
int b;
B() : b(99) {}
};
Everything else you've done (get/set, destructor) are accomplishing absolutely nothing. Your A class not only accomplishes just about as little, but does it even more poorly. Others have already pointed out the problem with A's constructor defining a local B object, and then leaking that object. None (that I've seen yet, anyway) has pointed out that even when you fix that, your definition of A will leak the B object anyway, because even though it creates a B object as part of creating an A object, it does not destroy the B object when the A object that contains it is destroyed.
I don't see any reason for your A class to dynamically allocate the B object at all (or, when you get down to it, to even exist). I'd define A more like this:
class A {
B b;
public:
void print() { std::cout << b.b << "\n";
};
It would be better, however, if a B object knew how to insert itself into a stream -- and if it used the normal syntax for that as well:
std::ostream &operator<<(std::ostream &os, B const &b) {
return os << b.b;
}
With this in place, your A class adds nothing at all, so your entire program becomes something like this:
struct B {
int b;
B() : b(99) {}
};
std::ostream &operator<<(std::ostream &os, B const &b) {
return os << b.b;
}
int main() {
std::cout << B() << "\n";
return 0;
}
Great tips guys, even though in retrospect I obfuscated my issue with naming the int variable b (should've been anything but b!!). That said, you guys "pointed" me in the direction to initialization lists, destructors, and ultimately to the topic of composition. Many thanks How to implement class composition in C++?

Understanding C++ Virtual Methods of Instances Allocated on the Stack

For the following code:
#include<iostream>
using namespace std;
class A{
public:
virtual int f(){return 1;}
};
class B : public A{
public:
virtual int f(){return 2;}
};
int main(int argc,char*argv[]){
A b=B();
cout<<b.f()<<endl;
}
I expect the number 2 to be printed. Instead the program prints the number 1.
Could someone explain why this is?
What you've done is called slicing. See What is object slicing?
Instead, pointers should be used:
A* b = new B();
The problem is that the type of your variable is A, and not B. The line:
A a = B();
creates a temporary of type B and then copies the A subpart of it into a that is of type A.
To use an object polymorphically you need to handle it through a pointer or reference:
int main() {
B b;
A &a = b; // an A reference to the B object
A *p = &b; // an A pointer to a B object
a.f(); // will dispatch to B::f
p->f(); // will dispacth to B::f
}
This is due to slicing. Runtime polymorphism only works through pointer or reference in C++. You can get virtual dispatch with another variable:
B b;
A& a = b;
cout << a.f() << endl;
Or you can directly assign a pointer, like here:
A* aptr = new B;
aptr->f();
delete aptr;
but here class B needs a virtual destructor. Though not strictly required in the first case, providing virtual destructor is a general rule for polymorphic types. GCC -Wnon-virtual-dtor flag helps you catch this error.
As others have stated, this is a case of slicing. The type of 'b' is 'A'. It is initialised via the implicitly defined default copy constructor for A:
A::A(const A& other);
This initialisation is valid because 'B' is derived from 'A'; i.e. it is an 'A'. Try implementing the copy constructor for 'A' explicitly to help understand what is going on.
Note that one practice to avoid this kind of issue is to ensure that all non-leaf classes are abstract.
You are observing what is called object slicing in c++. You can alleviate by using a pointer to the base:
int main(int argc,char*argv[]){
B b;
A* a=&b;
cout<<a->f()<<endl;
}
If you convert the stack variables into pointers, you will see that you get the correct answer. This has to do with the fact that b (an instance of A) is allocated on the stack, but remains type A, despite the fact that you copied B() over it.
#include<iostream>
using namespace std;
class A
{
public: virtual int f()
{
return 1;
}
};
class B : public A
{
public:
virtual int f()
{
return 2;
}
};
int main(int argc,char*argv[])
{
A * b = new B();
cout << b->f() << endl;
delete b;
}

Resources