I created some code to reproduce the problem:
#include "stdafx.h"
#include <iostream>
#include <vector>
class A
{
protected:
int m_X;
public:
A() {
std::cout << "in A ctor" << std::endl;
m_X = 0;
}
virtual void printX(){ std::cout << "in A " << m_X << std::endl; }
};
class B : public A
{
public:
B() {
std::cout << "in B ctor" << std::endl;
m_X = 1;
}
virtual void printX(){ std::cout << "in B " << m_X << std::endl; }
};
class As
{
public:
void AddA( const A &a ){ m_As.push_back( a ); }
void PrintXs()
{
for ( auto a : m_As )
{
a.printX();
}
}
private:
std::vector<A> m_As;
};
int _tmain(int argc, _TCHAR* argv[])
{
As as;
B b;
as.AddA( b );
as.PrintXs();
system("pause");
return 0;
}
The output of this is:
in A ctor
in B ctor
in A 1
I want "in B 1" instead of "in A 1". I'm sure my understanding of virtual is flawed. How must I change the code to call the B PrintX()? Note that there will be other classes that inherit from A so I really don't want to code a static call.
Thanks.
What you're doing is called slicing. This is where you take an object of a derived class and trim off everything that is not in the parent and assign it to the parent.
What you want to do is use polymorphism to do what you explained. To do this, change your vector from a copy of the object, to a ptr to the object.
If interested in more details, please use the links provided, the information included in them seems to be very complete.
The quick fix is to change your As class to the following:
class As
{
public:
void AddA( A &a ){ m_As.push_back( &a ); }
void PrintXs()
{
for ( auto a : m_As )
{
a->printX();
}
}
private:
std::vector<A*> m_As;
};
When you use std::vector<A> m_As;, the vector can only fit A objects. If you use pointers instead then polymorphism can work and call the correct printX function. However, this has the problem of dangling pointer if the lifetime of the pointed to object expires. To handle that it would be better to use a smart pointer class like std::unique_ptr.
Since you're passing objects by value you can not take advantages of polymorphism. Pass them by (smart) pointers or references.
std::vector<std::shared_ptr<A>> m_As;
// or
std::vector<std::unique_ptr<A>> m_As;
// or
std::vector<A*> m_As; // be careful of bare pointers
// or (since C++11)
std::vector<std::reference_wrapper<A>> m_As;
std::reference_wrapper magic!
For the last one, you can use std::reference_wrapper and std::ref:
class As
{
public:
void AddA(A &a){ m_As.push_back( std::ref(a) ); }
void PrintXs() const
{
for ( auto a : m_As )
{
a.get().printX();
}
}
private:
std::vector<std::reference_wrapper<A>> m_As;
};
Using last code, you don't have to change main code.
Live code
for ( const auto & a : m_As )
{
a.printX();
}
it will keep you from expanded copy and provide the B-instance instead of A-instance, appeared as copy.
Related
I would like to have a unique_ptr class member that points to the base class, but later in the constructor through polymorphism can be changed to point to a sister class that also derives from the same base class.
While I don't get any errors in the constructor setting this polymorphism, it does not seem to work correctly, since I get error messages that my polymorphic pointer can't find a member of the sister class to which I thought the pointer was now pointing.
How do I correctly achieve polymorphism here?
class A {
int bar;
};
class B : public A {
int foo;
};
class C: public A {
C();
std::unique_ptr<A> _ptr; // changing to std::unique_ptr<B> _ptr removes the "class A has no member 'foo'" error
};
C::C() : A()
{
_ptr = std::make_unique<B>(); // no errors here
int w = _ptr->foo; // class A has no member 'foo'
}
When you assign
_ptr = std::make_unique<B>();
This works because B is a derived class of A, however _ptr is still a unique_ptr to the base class. You can't change the type of a variable after it's declared.
So what are your options?
Because you know that _ptr stores a pointer to the derived class B, you can do a cast after dereferencing it:
_ptr = std::make_unique<B>();
// derefence the pointer, and cast the reference to `B&`.
B& reference_to_sister = (B&)(*_ptr);
int w = reference_to_sister.foo;
If you take this approach, you'll have to somehow keep track of which derived class is in _ptr, or you'll run the risk of running into bugs.
Alternatively, if you're using C++17, you can use std::variant:
class C : public A {
void initialize(A& a) {
// Do stuff if it's the base class
}
void initialize(B& b) {
// Do different stuff if it's derived
int w = b.foo;
}
C() {
_ptr = std::make_unique<B>(); // This works
// This takes the pointer, and calls 'initialize'
auto initialize_func = [&](auto& ptr) { initialize(*ptr); };
// This will call 'initialize(A&)' if it contains A,
// and it'll call 'initialize(B&)' if it contains B
std::visit(initialize_func, _ptr);
}
std::variant<std::unique_ptr<A>, std::unique_ptr<B>> _ptr;
};
In fact, if you use std::variant this will work even if A and B are completely unrelated classes.
Here's another short variant example
#include <variant>
#include <string>
#include <iostream>
void print(std::string& s) {
std::cout << "String: " << s << '\n';
}
void print(int i) {
std::cout << "Int: " << i << '\n';
}
void print_either(std::variant<std::string, int>& v) {
// This calls `print(std::string&) if v contained a string
// And it calls `print(int)` if v contained an int
std::visit([](auto& val) { print(val); }, v);
}
int main() {
// v is empty right now
std::variant<std::string, int> v;
// Put a string in v:
v = std::string("Hello, world");
print_either(v); //Prints "String: Hello, world"
// Put an int in v:
v = 13;
print_either(v); //Prints "Int: 13"
}
I want to create a vector of "Act" objects that contain pointers to either "Eat" or "Drink" dynamically allocated objects. The new objects are being emplaced like so:
action_vector.emplace_back(Act::BehaviorType::eat);
However, it is seg-faulting and I can't figure out why. I thought that emplace_back would implicitly call the move constructor, not the destructor, but for some reason it is, which (I think) is what is screwing everything up.
Is there any way to successfully create a vector of such objects?
Here is the rest of the code along with its output. Sorry if it's a little verbose, but basically it's just a strategy pattern.
#include <iostream>
#include <vector>
class IBehavior
{
public:
IBehavior() = default;
virtual ~IBehavior() = default;
virtual void execute() = 0;
};
class Drink : public IBehavior
{
public:
Drink(): IBehavior() {}
~Drink() {}
void execute() { std::cout << "Drinking" << std::endl; }
};
class Eat : public IBehavior
{
public:
Eat(): IBehavior() {}
~Eat() {}
void execute() { std::cout << "Eating" << std::endl; }
};
class Act
{
IBehavior * b;
public:
enum class BehaviorType { eat = 0, drink = 1 };
Act() = default;
~Act()
{
std::cout << "Calling the destructor" << std::endl;
delete b;
}
Act(BehaviorType b_type) { SetBehavior(b_type); }
Act(Act&& act)
{
std::cout << "Calling the move constructor" << std::endl;
this->b = act.b;
}
void SetBehavior(BehaviorType b_type)
{
if(b_type == BehaviorType::eat) b = new Eat();
if(b_type == BehaviorType::drink) b = new Drink();
}
void execute() { b->execute(); }
};
int main(int argc, char * argv[])
{
std::vector<Act> action_vector;
for(int i = 0; i < 10; ++i)
{
action_vector.emplace_back(Act::BehaviorType::eat);
action_vector[i].execute();
}
return 0;
}
output:
Eating
Calling the move constructor
Calling the destructor
Eating
Calling the move constructor
Calling the move constructor
Calling the destructor
Calling the destructor
Segmentation fault: 11
Your move constructor copies b, and destructor deletes b, so if you move construct an instance then the same pointer value will be deleted twice which has undefined behaviour.
General solution: Use a smart pointer.
Another bug: Default constructor leaves b uninitialised. When a default constructed object is destroyed, the uninitialised pointer is deleted and behaviour is undefined. Smart pointer fixes this also.
Really bad title, couldn't think of how to word it, sorry.
So say I had the following code:
class A {
virtual int getSize() {
return 0;
}
}
class B : public A {
int getSize() {
return 32;
}
}
void doStuff(A a) {
std::cout << a.getSize() << std::endl;
}
int main() {
B b;
doStuff(b);
}
It would print out 0, however I want it to print out 32. In other words, I want to pass it the class and it prints out that classes function, so I could create a class C, where the size is 64, and if I pass that C instance to the doStuff function, I want it to print 64.
Is there any way I can do this in C++, would I have to use templates or some fancy C++ feature I don't know about?
A one-byte patch:
void doStuff(A &a) {
std::cout << a.getSize() << std::endl;
}
Your version takes the argument by value, which means that the function makes a copy of b (a copy which is an A) and then calls the copy's getSize(). In this version, the function takes the argument by reference, and calls b's own getSize(), which is B::getSize().
You should use pointers, or even better: smart pointers! That way, the function of the runtime type gets called. It's a basic example of polymorhpism. If you want to avoid pointers, Beta's slicing approach is equally valid.
#include <iostream>
#include <memory>
class A {
virtual int getSize() {
return 0;
}
}
class B : public A {
virtual int getSize() {
return 32;
}
}
void doStuff(std::shared_ptr<A> a) {
std::cout << a->getSize() << std::endl;
}
int main() {
std::shared_ptr<A> b(new B());
doStuff(b); // Will output '32'.
}
This should correctly call the function as implemented by B.
Slicing the object is one approach, and in addition I think you're asking for, I think, a pretty straightforward use of polymorphism in C++. http://www.cplusplus.com/doc/tutorial/polymorphism/
That's almost immediately applicable, just call your class A Shape, and B and C could be Square and Triangle. Your DoStuff function could take a pointer to a Shape, then you can pass it a triangle or a square, and when you deference the Shape in the function, it will call the correct function.
So you'd have (also you need to make the members public, I think):
class A {
public:
virtual int getSize() {
return 0;
}
};
class B : public A {
public:
int getSize() {
return 32;
}
};
void doStuff(A* a) {
std::cout << a->getSize() << std::endl;
}
int main() {
B b;
doStuff(&b);
}
According to N3290, std::unique_ptr accepts a deleter argument in its constructor.
However, I can't get that to work with Visual C++ 10.0 or MinGW g++ 4.4.1 in Windows, nor with g++ 4.6.1 in Ubuntu.
I therefore fear that my understanding of it is incomplete or wrong. I can't see the point of a deleter argument that's apparently ignored, so can anyone provide a working example?
Preferably I'd like to see also how that works for unique_ptr<Base> p = unique_ptr<Derived>( new Derived ).
Possibly with some wording from the standard to back up the example, i.e. that with whatever compiler you're using, it actually does what it's supposed to do?
This works for me in MSVC10
int x = 5;
auto del = [](int * p) { std::cout << "Deleting x, value is : " << *p; };
std::unique_ptr<int, decltype(del)> px(&x, del);
And on gcc 4.5, here
I'll skip going to the standard, unless you don't think that example is doing exactly what you'd expect it to do.
To complement all previous answers, there is a way to have a custom deleter without having to “pollute” the unique_ptr signature by having either a function pointer or something equivalent in it like this:
std::unique_ptr<MyType, myTypeDeleter> // not pretty
This is achievable by providing a full specialization to the std::default_delete class template, like this:
template<>
class std::default_delete<MyType>
{
public:
void operator()(MyType *ptr) const
{
delete ptr;
}
};
And now all std::unique_ptr<MyType> that “sees” this specialization will be deleted with it. Just be aware that it might not be what you want for all std::unique_ptr<MyType>, so chose carefully your solution.
My question has been pretty well answered already.
But just in case people wondered, I had the mistaken belief that a unique_ptr<Derived> could be moved to a unique_ptr<Base> and would then remember the deleter for the Derived object, i.e., that Base would not need to have a virtual destructor. That was wrong. I'd select Kerrek SB's comment as "the answer", except one cannot do that for a comment.
#Howard: the code below illustrates one way to achieve what I believed the cost of a dynamically assigned deleter had to mean that unique_ptr supported out of the box:
#include <iostream>
#include <memory> // std::unique_ptr
#include <functional> // function
#include <utility> // move
#include <string>
using namespace std;
class Base
{
public:
Base() { cout << "Base:<init>" << endl; }
~Base() { cout << "Base::<destroy>" << endl; }
virtual string message() const { return "Message from Base!"; }
};
class Derived
: public Base
{
public:
Derived() { cout << "Derived::<init>" << endl; }
~Derived() { cout << "Derived::<destroy>" << endl; }
virtual string message() const { return "Message from Derived!"; }
};
class BoundDeleter
{
private:
typedef void (*DeleteFunc)( void* p );
DeleteFunc deleteFunc_;
void* pObject_;
template< class Type >
static void deleteFuncImpl( void* p )
{
delete static_cast< Type* >( p );
}
public:
template< class Type >
BoundDeleter( Type* pObject )
: deleteFunc_( &deleteFuncImpl< Type > )
, pObject_( pObject )
{}
BoundDeleter( BoundDeleter&& other )
: deleteFunc_( move( other.deleteFunc_ ) )
, pObject_( move( other.pObject_ ) )
{}
void operator() (void*) const
{
deleteFunc_( pObject_ );
}
};
template< class Type >
class SafeCleanupUniquePtr
: protected unique_ptr< Type, BoundDeleter >
{
public:
typedef unique_ptr< Type, BoundDeleter > Base;
using Base::operator->;
using Base::operator*;
template< class ActualType >
SafeCleanupUniquePtr( ActualType* p )
: Base( p, BoundDeleter( p ) )
{}
template< class Other >
SafeCleanupUniquePtr( SafeCleanupUniquePtr< Other >&& other )
: Base( move( other ) )
{}
};
int main()
{
SafeCleanupUniquePtr< Base > p( new Derived );
cout << p->message() << endl;
}
This works. The destruction happens properly.
class Base
{
public:
Base() { std::cout << "Base::Base\n"; }
virtual ~Base() { std::cout << "Base::~Base\n"; }
};
class Derived : public Base
{
public:
Derived() { std::cout << "Derived::Derived\n"; }
virtual ~Derived() { std::cout << "Derived::~Derived\n"; }
};
void Delete(const Base* bp)
{
delete bp;
}
int main()
{
std::unique_ptr<Base, void(*)(const Base*)> ptr = std::unique_ptr<Derived, void(*)(const Base*)>(new Derived(), Delete);
}
I have a class Model:
class Model
{
...
boost::shared_ptr<Deck> _deck;
boost::shared_ptr<CardStack> _stack[22];
};
Deck inherits from CardStack.
I tried to make _stack[0] point to the same thing that _deck points to by going:
{
_deck = boost::shared_ptr<Deck>(new Deck());
_stack[0] = _deck;
}
It seems that the assignment to _deck of _stack[0] results in a copy of _deck being made. (I know this because modifications to _stack[0] do not result in modifications to _deck.) How can I get them to point to the same thing?
Ok - no copy constructor is being called. I have verified this by implementing it and seeing if it gets called - it doesn't.
However - I have a function that operates on CardStack objects:
void TransferSingleCard(CardStack & src, CardStack & dst, Face f)
{
if( !src._cards.empty() )
{
src._cards.back().SetFace(f);
dst.PushCard(src._cards.back());
src._cards.pop_back();
}
}
Now - when I call:
{
TransferSingleCard(*_stack[DECK], _someotherplace, FACEDOWN);
std::cout << *_stack[DECK];
std::cout << *_deck;
}
I get this output (where std::cout on a CardStack will print out the size of that stack):
Num(103) TOP
Num(104) TOP
... so I've concluded (incorrectly?) that _stack[DECK] points to something different.
The Deck
class Deck : public CardStack
{
public:
Deck(int numsuits=2, StackIndex index = NO_SUCH_STACK );
Deck::Deck( const Deck & d);
int DealsLeft() const;
void RecalcDealsLeft();
private:
int _dealsleft;
};
Not clear what you are asking about - consider this code:
#include <iostream>
#include "boost/shared_ptr.hpp"
using namespace std;
struct A {
virtual ~A() {
cout << "destroyed" << endl;
}
};
struct B : public A {
};
int main() {
boost::shared_ptr<B> b( new B );
boost::shared_ptr<A> a;
a = b;
}
Only one "destroy" message appears, indicating that no copy has been made.
This example - derives from #Neil's answer, tries to emulate what you say is happening. Could you check that it works as expected (A and B have the same count) on your system.
Then we could try and modify this code or your code until they match.
#include <boost/shared_ptr.hpp>
#include <iostream>
class A {
public:
virtual ~A()
{
std::cerr << "Delete A" << std::endl;
}
int _count;
void decrement()
{
_count --;
}
};
class B : public A {
public:
virtual ~B()
{
std::cerr << "Delete B" << std::endl;
}
};
int main()
{
boost::shared_ptr<B> b(new B);
b->_count = 104;
boost::shared_ptr<A> a;
a = b;
a->decrement();
std::cerr << "A:" << a->_count << std::endl;
std::cerr << "B:" << b->_count << std::endl;
return 0;
}
EDIT:
So from the comment, we know the original pointers are correct, so now we need to trace.
Either:
log pointers to see when they change.
Use watchpoints in a debugger to see when the pointer changes.
Use a third shared pointer to see which pointer is changed.
Introduce a function that changes both pointers at the same time.
I think the problem is that you're assigning between different types here. boost::shared_ptr is a template and templates are not polymorphic even if the type in them is. So what's happening is that your compiler sees the assignment from boost::shared_ptr<Deck> to boost::shared_ptr<CardStack> and notices that it can make the assignment by calling the copy constructor for CardStack to duplicate the Deck object.
I think what you want the assignment to look like is something like this:
_stack[0] = boost::static_pointer_cast<CardStack>(_deck);
Which will do the conversion the way you expect it to.
I think you may want shared_array for _stack . . . Take a look at the documentation on shared_ptr;from boost.org, specifically:
http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm
"Normally, a shared_ptr cannot
correctly hold a pointer to a
dynamically allocated array. See
shared_array for that usage."
Also, be aware of the T* get() function (not to be used without good reason) which returns the raw pointer being held by the managed pointer (shared_ptr in this case).