I would like to use virtual functions of objects of different classes (derived from the same base class) without a) constructing all the objects or b) using new. Please see the code example:
#include <iostream>
class A{
public:
virtual void p(void){std::cout << "Im A" << std::endl;};
};
class B : public A{
public:
virtual void p(void) override {std::cout << "Im B" << std::endl;};
};
class C : public A{
public:
virtual void p(void) override {std::cout << "Im C" << std::endl;};
};
int main(){
bool cond = true; // some condition
A* o1;
if (cond) o1 = new B(); else o1 = new C();
o1->p(); // will call correct p(), i.e. either B::p or C::p but not A::p
A o2 = B();
o2.p(); // will call A::p
A* o3;
B tmp1; C tmp2; // construct both objects altough only one is needed
if (cond) o3 = &tmp1; else o3 = &tmp2;
o3->p(); // will call correct p(), i.e. either B::p or C::p but not A::p
A* o4;
if (cond) {B tmp; o4 = &tmp;} else {C tmp; o4 = &tmp;} // uses address of local variable
o4->p(); // will call correct p(), i.e. either B::p or C::p but not A::p
return 0;
}
I want the behavior of o1 but without calling new. o2 doesnt work (calls function of base class and if base class is abstract it doesnt work at all). o3 works but constructs all the different objects although only one is needed. o4 kinda "works" but uses a reference to a local variable outside of its scope.
What would be the correct / best / modern C++ way to do it?
It will be better to use a helper function when you want to avoid using new but be able to use different derived types based on some condition.
void do_stuff(A& obj)
{
}
int main()
{
bool cond = true; // some condition
if (cond)
{
B tmp;
do_stuff(tmp);
}
else
{
C tmp;
do_stuff(tmp);
}
return 0;
}
How to use virtual functions in derived objects without new
Like this:
B b;
C c;
A& a = cond ? b : c;
a.p();
without a) constructing all the objects
You can also do this:
if (cond) {
B b;
b.p();
} else {
C c;
c.p();
}
At this point it doesn't really matter that the function is virtual though since we are using static dispatch. Which is better than dynamic dispatch.
o4 kinda "works" but uses a reference to a local variable outside of its scope.
I.e. the behaviour of the program is undefind i.e. it doesn't work at all.
If using placement new is acceptable, you can use a union to hold the storage, then construct the proper member based on the condition:
union U {
U() { }
B b;
C c;
};
int test(bool cond) {
U u;
A *a2;
if (cond)
a2 = new (&u.b) B;
else
a2 = new (&u.c) C;
a2->p();
return 0;
}
The constructor in the union is necessary since the default constructor will be implicitly deleted due to the presence of non-trivial default constructors for the members of the union.
You'll also have to manually destroy your objects when you're done.
For starters this code snippet
A* o4;
if (cond) {B tmp; o4 = &tmp;} else {C tmp; o4 = &tmp;} // uses address of local variable
o4->p(); // will call correct p(), i.e. either B::p or C::p but not A::p
has undefined behavior because outside the if statement the pointer o4 is invalid due to the fact that the local object tmp is not alive outside the scopes of the if sub-statement.
As for this code snippet
A o2 = B();
o2.p(); // will call A::p
then you should use a reference to an object of the type B. For example
B b;
A &o2 = b;
o2.p();
As for the code snippet with the pointer o1 then in any case pointed objects must exists. Either they will be created dynamically as in your example or you can create the both locally and then depending on the condition set the pointer to one of them.
If you're worried about using new because of memory management, just use a std::shared_ptr instead:
int main(){
bool cond = true; // some condition
std::shared_ptr<A> o1;
if(cond) {
o1 = std::make_shared<B>();
} else {
o1 = std::make_shared<C>();
}
o1->p();
return 0;
}
Prints:
Im B
Working example
Note: I was originally going to suggest an std::unique_ptr instead, however I didn't think they were copyable. But I was wrong. std::unique_ptrs will work too.
Note 2: you will need C++14 or later to use std::make_shared() or std::make_unique, but new will work in C++11. That violates your rule of not using new, but at least it does manage memory for you.
If you want to avoid new mainly to avoid the extra cleanup code and potential bugs from dangling pointers, double deletes, etc., just use std::unique_ptr:
std::unique_ptr<A> o1;
if (cond)
o1 = std::make_unique<B>();
else
o1 = std::make_unique<C>();
o1->p();
This will do essentially the same as the code using new, but behind the scenes, and also does the delete for you when o1 goes out of scope.
If you have a performance-critical code bottleneck and want to avoid the overhead of the heap memory allocation and deallocations used by new, things get trickier, but you could do it with something like:
using B_or_C_type = std::variant<B,C>;
auto o5 = cond ? B_or_C_type{B{}} : B_or_C_type{C{}};;
std::visit(std::mem_fn(&A::p), o5);
On typical systems, this code will avoid using any heap memory at all, instead using stack memory large enough to hold the larger of B or C. std::variant has the logic to act like a union but making sure only the correct type actually gets used. std::visit applies any functor to a variant, as long as the functor can be called with every type in the variant. Note if you define a variant variable with no initializer, it creates an object with the first type, though it can be reassigned to a different-typed object later. (If creating that default object isn't possible or should be avoided, you could use std::monostate as the first dummy type - but then using std::visit gets trickier.)
Related
I am trying to understand the preferred approach for a class to handle the validity of (reference to) the object of another class.
In here, C has a vector that stores references of D objects. If D and C are a part of the library, how should C handle the case where a D object goes out of scope in the caller?
A couple approaches that I have in mind though not sure of the feasibility:
D knows about what _list stores and as soon as the said D goes out of scope, ~D() runs which removes itself from _list.
_list stores weak_ptr instead of a raw and upon any access to D, you invoke weak_ptr::lock() prior to accessing, though this will require the instantiation of a shared_ptr which seems to be not so common in production?
struct D
{
~D()
{
printf ("~D()\n");
}
};
class C
{
vector<D*> _list;
public:
void add(D* dObject)
{
_list.push_back(dObject);
printf ("Adding D => size = %ld\n", _list.size());
}
~C()
{
printf ("~C()\n");
}
};
int main()
{
C c1;
{
D d1;
c1.add(&d1);
}
/**
_list[0] is garbage now. How to avoid accessing it i.e
C being aware to not access it?
*/
printf ("----out of scope---\n");
D d2;
c1.add(&d2);
}
Alternatives are
Using scope_exit (also called scope guard):
int main() {
C c1;
{
D d1;
c1.add(&d1);
scope_exit d1_erase([&] {
c1.erase(&d1);
});
// ... insert here other code within the inner scope
}
// ... insert here other code within the outer scope
return 0;
}
You should use a std::set instead of a std::vector within D to more efficiently find and erase entries again. And of course D should publicly provide an erase or delete function.
scope_exit is part of the soon-to-be-published C++ Library Fundamentals v3 Technical Specification (https://en.cppreference.com/w/cpp/experimental/scope_exit). The implementation belonging to the accepted proposal is here: https://github.com/PeterSommerlad/SC22WG21_Papers/tree/master/workspace/P0052_scope_exit.
Creating a custom class managing adding and automatically deleting (also at scope exit) the pointer to and from the list from outside both classes:
class E
{
public:
E(C& c, D* dp) : _c(c), _dp(dp) { _c.add(_dp); }; // add to list
~E() { _c.erase(_dp); }; // remove from list
E(const E&) = delete; // rule-of-3
E& operator=(const E&) = delete;
private:
C& _c;
D* _dp;
};
int main() {
C c1;
{
D d1;
E e(c1, &d1); // adds pointer to d1 to c1 list
// and removes it automatically at the end of the scope
}
return 0;
}
You could also store D within E instead of referring to it by stored reference.
BTW: Destruction order for local scopes is in reverse order to construction.
Those two solutions keep C and D as they are. You have to decide yourself, whether C intrusively should have knowledge of its D object or whether its addition and removal from D is handled non-intrusively like in those two solutions.
I would not use a shared_ptr/weak_ptr for those cases, where the objects are stored locally and the time of destruction is clear (when leaving scope either at its end or due to an exception or a return statement).
Basically in c++, you should not keep any reference of the stack instance to some containers. It is dangerous with dangling memory accesses and you should add instances which is created on heap to your preferred container. In this case of, shared_ptr is useful and safe for managing the lifetime of your instance.
#include <vector>
#include <string>
#include <memory>
using namespace std;
struct D{
public:
string _name;
D(const string& name) : _name(name){}
~D(){
printf("~D()\n");
}
};
class C{
vector<shared_ptr<D>> _list;
public:
void add(shared_ptr<D> dObject){
_list.push_back(dObject);
printf("Adding D => size = %ld\n", _list.size());
}
void print() {
for (const auto& d : _list) {
printf("%s\n", d->_name.c_str());
}
}
~C(){
printf("~C()\n");
}
};
int main(){
C c1;
{
c1.add(make_shared<D>("first D"));
}
c1.add(make_shared<D>("second D"));
c1.print();
}
output:
Adding D => size = 1
Adding D => size = 2
first D
second D
~C()
~D()
~D()
Additional: remove an object by block scope
C c1;
{
auto d = make_shared<D>("first D");
shared_ptr<nullptr_t> D_deleter{ nullptr, [&](auto) { c1.remove(d); } };
c1.add(d);
} // d would be deleted here
Essentially, the original question is asking: "how do I know when a local variable goes out of scope?"
The answer is: when its destructor is called.
For the code in your question, you could have ~D() to remove itself from c1.
This is awkward, and the discussion beneath the question brings up std::shared_ptr,
but the code in the example uses a local variable. You can't use std::shared_ptr
on a local variable, only on a heap variable. This is not made clear in the discussion.
Here are two references to questions specifically related to shared_ptr and local variables:
Create shared_ptr to stack object
and
Set shared_ptr to point existing object
A better solution to your problem is to not use a local variable, but a heap variable.
The difference between local and heap variables seems to be causing some confusion.
C++ has several storage classes for variables:
stack - used for local variables and function parameters
register - sometimes used for local variables - primarily an optimization
global/static - "one and only one", in space allocated at program load time.
thread local - global/static but thread specific - each thread has its own copy.
heap - allocated by the program, on the fly, via new or malloc (or equivalent). Sometimes called "free store".
C++ has some tricks with new and delete where you can use a custom heap,
which is memory you're managing yourself, but that memory will have been allocated
from the general heap, or possibly exist inside other variables.
Here are three different examples:
A local variable:
void some_function(C& c1)
{
D d1;
c1.add(&d1);
}
When this function exits, the stack frame that contains d1 goes away,
and the variable ceases to exist. ~D() will get called,
and you could remove d1 from c1 if you added
a mechanism to do this, like making d1 hold a pointer to c1, etc.,
which is awkward, but possible.
std::shared_ptr will not help with a local variable.
A heap variable allocated with new:
void some_function(C& c1)
{
D *d1p = new D();
c1.add(d1p);
}
When this function exits, the D variable that d1p points to still exists.
It will continue exist until it is explicitly deleted. In this context,
the c1 variable "owns" this pointer, and it responsible for deleting it
when it has finished using it.
This is the case where adding a smart pointer can simplify reference management,
as in:
A heap variable and shared_ptr created via make_shared:
void some_function(C& c1)
{
std::shared_ptr<D> d1p = std::make_shared<D>();
c1.add(d1p);
}
This example creates a D variable on the heap, and creates a local shared_ptr
holding a reference to that D variable, via a small counter object,
which is also on the heap. When the shared_ptr is passed to the C::add method,
the reference count in the counter object is incremented. When the shared_ptr
goes out of scope, the reference count in the counter object is decremented.
The shared_ptr in the c1 object now holds the only reference to that shared_ptr,
and when the c1 object is destroyed, the shared_ptr it contains will be destroyed,
releasing its reference - if the count goes to zero, the object it points to will
also be destroyed.
Besides this automatic destruction, another advantage of this approach is that you can get references to this D variable
from c1 and use them elsewhere, and these references (and the D variable itself)
can outlive c1.
The C class has to be modified for this case, e.g.
std::vector<std::shared_ptr<D>> _list;
and
void add(std::shared_ptr<D> dObject)
Here's a reference to how shared pointers work:
How do shared pointers work?
and some documentation on make_shared:
https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared
Does copy elision kick in in this situation? In other words, do modern compilers with copy elision avoid to call any copy constructor here?
class BigObj{};
BigObj fun()
{
BigObj b;
return b;
}
int main()
{
BigObj *pb = new BigObj(fun());
}
I aim to store an object in a pointer. The object is returned by a function. I want to store it without copying it.
I can't use c++11
IMO it is not entirely clear what you aim to achieve. If dynamic allocation is what you want to use, then your function should simply:
BigObj * fun()
{
return new BigObj;
}
int main()
{
BigObj *pb = fun();
}
... and save yourself the trouble.
Contrary to the previous revision of the answer, it turned out that the compiler can omit a substantial amount of work as long as it is in a static context that can be thoroughly analyzed:
class C {
public:
C() {qDebug() << "C created";}
C(const C & o) { qDebug() << "C copied"; }
};
C foo() {
C c;
qDebug() << &c;
return c;
}
...
C c = C(foo()); // note that `C c = ` is also copy construction
qDebug() << &c;
The output verifies that both instances have the same address, so even in the context of a local, the instance is actually not stored in the stack frame of foo.
Changing to:
C * cp = new C(foo());
qDebug() << cp;
to my surprise also output the same address, with both the return by value copy and the copy constructor omitted. c in foo is constructed directly in the memory chunk, allocated by new.
In conclusion the C++ compiler is pretty smart at analyzing and doing every possible optimization.
Turning off optimizations in the first and second case respectively:
C created
0x28fd97
C copied
C copied
C copied
0x28fdeb
...
C created
0x28fd97
C copied
C copied
0x34fd00
RVO is among those things that the standard permits, but does not specifically require. That said, most modern compilers (at least, with appropriately optimisation settings enabled) will implement it. If you want a guarantee, however, you will need to read your compiler documentation.
Since the aim is to dynamically allocate an object anyway, I would simply change the example so the called function does dynamic allocation. Instead of (the OP's code);
BigObj fun()
{
BigObj b;
// presumably the point of fun() is that some initialisation
// of b occurs here
return b;
}
int main()
{
BigObj *pb = new BigObj(fun());
}
I would simply use
BigObj *fun()
{
BigObj *b = new BigObj;
// presumably the point of fun() is that some initialisation
// of *b occurs here
return b;
}
int main()
{
BigObj *pb = fun();
}
and eliminate the potential copying of a BigObj all together. The only thing that is being copied around is the value of a pointer. The compiler therefore does not rely on presence of C++11 move constructors to optimise the above, since it avoids unnecessary creation and copying of objects, so this meets the OPs need to not use C++11.
Obviously, in either case, it would be good practice to match the usage of operator new with a corresponding operator delete.
Suppose I have:
class A { };
class B : public A { };
A f() {
if (Sunday())
return A;
else
return B;
}
Obviously this doesn't work since A's copy constructor would be called. Is there anyway to return stack allocated object without losing it's type?
I've tried using std::shared_ptr<A> but it got me into another issue since std::shared_ptr<B> isn't std::shared_ptr<A>.
It is not immediately possible to return a stack-allocated (i.e. local) object out of the function that created that object. Local objects are destroyed on function return. You can hide/obfuscate the actual nature of the object's allocation by using various "smart pointers" and similar techniques, but the object itself should be allocated dynamically.
Other that that, as long as the local object lifetime rules are obeyed, polymorphism for local objects works in exactly the same way as it works for any other objects. Just use a pointer or a reference
A a;
B b;
A *p = Sunday() ? &a : &b;
// Here `*p` is a polymorphic object
Pointer p in the above example remains valid as long as the local object lives, which means that you cannot return p from a function.
Also, as you see in the example above, it unconditionally creates both objects in advance, and then chooses one of the two while leaving the second one unused. This is not very elegant. You cannot create different versions of such object in different branches of if statement for the very same reasons for which you cannot return a local object from a function polymorphically: once the local block that created the object is complete, the object is destroyed.
The latter problem can be worked around by using a raw buffer and manual in-place construction
alignas(A) alignas(B) char object_buffer[1024];
// Assume it's big enough for A and B
A *p = Sunday() ? new(buffer) A() : new (buffer) B();
// Here `*p` is a polymorphic object
p->~A(); // Virtual destructor is required here
but it does not look pretty. A similar technique (involving copying of the buffer) can probably be used to make local polymorphic objects survive block boundaries (see #Dietmar Kühl's answer).
So, again, if you want to create only one object of the two and have your object to survive block boundaries, then immediate solutions put local objects are out of the question. You will have to use dynamically allocated objects.
It's not possible because of slicing. Use std::unique_ptr instead. You won't lose the dynamic type, but it will be accessible only through the interface of A.
The easiest approach is certainly to use a suitable smart pointer, e.g., std::unique_ptr<A>, as the return type and to allocate the object on the heap:
std::unique_ptr<A> f() {
return std::unique_ptr<A>(Sunday()? new B: new A);
}
For the approach returning a std::unique_ptr<A> which may point to a B, it is necessary that A has a virtual destructor as otherwise the code may result in undefined behavior when the std::unique_ptr<A> actually points to a B object. If A doesn't have a virtual destructor and can't be changed, the problem can be avoided by using a suitable std::shared_ptr<...> or by using a suitable deleter with the std::unique_ptr<...>:
std::unique_ptr<A, void(*)(A*)> f() {
if (Sunday()) {
return std::unique_ptr<A, void(*)(A*)>(new B, [](A* ptr){ delete static_cast<B*>(ptr); });
}
else {
return std::unique_ptr<A, void(*)(A*)>(new A, [](A* ptr){ delete ptr; });
}
}
If you don't want to allocate the objects on the heap, you can use a holder type which stores a union with A and B which is then appropriately constructed and destructed (the code below assumes that the copy of A or B won't throw an exception; if necessary, suitable move construction and move assignment can be added):
class holder {
bool is_b;
union {
A a;
B b;
} element;
public:
holder(): is_b(Sunday()) {
if (this->is_b) {
new(&this->element.b) B();
}
else {
new(&this->element.a) A();
}
}
holder(holder const& other) { this->copy(other); }
void copy(holder const& other) {
this->is_b = other.is_b;
if (this->is_b) {
new(&this->element.b) B(other.element.b);
}
else {
new(&this->element.a) A(other.element.a);
}
}
~holder() { this->destroy(); }
void destroy() {
if (this->is_b) {
this->element.b.~B();
}
else {
this->element.a.~A();
}
}
holder& operator= (holder const& other) {
this->destroy();
this->copy(other);
return *this;
}
operator A const&() const { return this->is_b? this->element.b: this->element.a; }
operator A&() { return this->is_b? this->element.b: this->element.a; }
};
I have't coded in c++ for some time and I got stuck when I tried to compile this simple snippet:
class A
{
public:
void f() {}
};
int main()
{
{
A a;
a.f(); // works fine
}
{
A *a = new A();
a.f(); // this doesn't
}
}
It's a pointer, so instead try:
a->f();
Basically the operator . (used to access an object's fields and methods) is used on objects and references, so:
A a;
a.f();
A& ref = a;
ref.f();
If you have a pointer type, you have to dereference it first to obtain a reference:
A* ptr = new A();
(*ptr).f();
ptr->f();
The a->b notation is usually just a shorthand for (*a).b.
A note on smart pointers
The operator-> can be overloaded, which is notably used by smart pointers. When you're using smart pointers, then you also use -> to refer to the pointed object:
auto ptr = make_unique<A>();
ptr->f();
Allow an analysis.
#include <iostream> // not #include "iostream"
using namespace std; // in this case okay, but never do that in header files
class A
{
public:
void f() { cout<<"f()\n"; }
};
int main()
{
/*
// A a; //this works
A *a = new A(); //this doesn't
a.f(); // "f has not been declared"
*/ // below
// system("pause"); <-- Don't do this. It is non-portable code. I guess your
// teacher told you this?
// Better: In your IDE there is prolly an option somewhere
// to not close the terminal/console-window.
// If you compile on a CLI, it is not needed at all.
}
As a general advice:
0) Prefer automatic variables
int a;
MyClass myInstance;
std::vector<int> myIntVector;
1) If you need data sharing on big objects down
the call hierarchy, prefer references:
void foo (std::vector<int> const &input) {...}
void bar () {
std::vector<int> something;
...
foo (something);
}
2) If you need data sharing up the call hierarchy, prefer smart-pointers
that automatically manage deletion and reference counting.
3) If you need an array, use std::vector<> instead in most cases.
std::vector<> is ought to be the one default container.
4) I've yet to find a good reason for blank pointers.
-> Hard to get right exception safe
class Foo {
Foo () : a(new int[512]), b(new int[512]) {}
~Foo() {
delete [] b;
delete [] a;
}
};
-> if the second new[] fails, Foo leaks memory, because the
destructor is never called. Avoid this easily by using
one of the standard containers, like std::vector, or
smart-pointers.
As a rule of thumb: If you need to manage memory on your own, there is generally a superiour manager or alternative available already, one that follows the RAII principle.
Summary: Instead of a.f(); it should be a->f();
In main you have defined a as a pointer to object of A, so you can access functions using the -> operator.
An alternate, but less readable way is (*a).f()
a.f() could have been used to access f(), if a was declared as:
A a;
a is a pointer. You need to use->, not .
If I understand slicing correctly I don't think this could happen with pointers or smart pointers. For example, if you had:
class A
{
int something;
};
class B : public A
{
int stuff;
int morestuff;
};
int main()
{
std::shared_ptr<B> b(new B());
std::shared_ptr<A> a;
a = b;
}
My understanding is that the block of memory allocated to the object pointed to by "b" is still the same and doesn't change when assigned to the smart pointer "a".
Please confirm or reject my understanding, or let me know of any pitfalls associated with this.
A smart pointer is still a pointer, so such an assignment won't cause slicing. Slicing happens only when dealing with values, not pointers. Note, however, templates don't know about the relationships between the items the point at, so even though B derives from A, shared_pointer<B> doesn't derived from shared_pointer<A>, so an assignment doesn't (automatically) get an automatic up-cast like it would with native pointers.
Edit: elaborating on final point.
Slicing happens with values, not pointers, so (given your definitions of A and B), something like:
A ax = b;
would work, but would "slice" the B object to become an A object. If, however, you have some sort of template that holds an instance of the item:
template <class T>
class holder {
T t_;
public:
holder &operator=(T const &t) {
t_ = t;
return *this;
}
holder &operator=(holder const &t) { t_ = t; return *this; }
};
Now, if we try to assign one value to another, like would cause slicing:
holder<A> ha;
holder<B> hb;
A a;
B b;
ha = a;
hb = b;
ha = hb;
we will NOT get slicing. Instead, the compiler will simply give us an error, telling us that holder<A> and holder<B> are not related types, so the assignment can't happen -- without adding an explicit cast, it simply won't compile.
You're correct, but they're not the same: you can't evaluate a->stuff.