I am relatively new to the OO side of C++, so please forgive this question, which I imagine is relatively straight-forward;
I have some code, loosely like this
SuperClass* super;
if (useSub1())
{
SubClass1 sub1 (s);
super = &sub1;
}
else if (useSub2())
{
SubClass2 sub2 (s);
super = &sub2;
}
super.someMethod();
It should be noted, that the point here is that the Construction of 'sub' is dependent upon the result of the call to someFunction().
Furthermore, there will be multiple if (...) { } constructs like above, each creating a different Object based on a different SubClass of SuperClass, based on some condition.
In summary then SuperClass has a number of Sub-classes, and I want to create the appropriate object type based on an if condition, then use the 'super' object to manipulate them.
Here is my question, I think by doing what I have sub1 (or sub2 for that matter) will go out of scope, so does this leave me with a pointer to nothing?
I hope it is reasonably clear what I am trying to do, I am just not sure how to code it correctly. Any help or advice is gratefully received.
Thanks in anticipation
Read about creational patterns, according to the information in your question: factory or factory method might suit your needs.
That design patterns allows to create objects dynamically in the heap, and you can operate with created object using pointer to a base class.
To prevent memory leaks and properly handle resources I suggest you to use smart pointers:
std::unique_ptr<Type> or boost::shared_ptr<Type>
if you are going to use std::auto_ptr don't use it with containers
As #Ben indicates: use new
SuperClass* super;
if (useSub1()) {
super = new SubClass1(s);
}
else if (useSub2()) {
super =new SubClass2(s)
}
super->someMethod();
Yes, sub1 and sub2 object dtors will be invoked as they go out of scope, and the 'super' pointer will be left pointing to an object in stack of indeterminate state. In some cases, it may still be in valid memory regions, hence not resulting in a segment violation, leading to a subtle bug.
It also looks like you need something like an 'Abstract Factory' design pattern:
http://en.wikipedia.org/wiki/Abstract_factory_pattern
You can use a factory method:
std::unique_ptr<SuperClass> create(const std::string& name)
{
if (name == "Sub1")
return std::unique_ptr<SubClass1>{new SubClass1};
if (name == "Sub2")
return std::unique_ptr<SubClass2>{new SubClass2};
throw std::runtime_error;
}
Here I've used an std::string to choose the type of the object to create, but it could be an enum instead.
Note that std::unique_ptr manages the lifetime of the object by itself, so there is no need to delete anything.
void foo()
{
auto object = create("SubClass1");
object->bar();
} <-- object is deleted automatically
Related
I'm studying C++ and in all my "experiments" i'm trying to understand temporary objects (rvalues) lifetime.
My question is:
Having an object which contains a const char* pointer, what happens when i want to use a constructor which takes "const char*" as argument? Usually temporary objects get destructed automatically but what happens with pointers created in this way?
I'm not using std::string or other c++11 classes for now because I'm just focusing on understanding rvalueness.
An example below:
class MyAwesomeClass {
private:
const char* data;
public:
MyAwesomeClass(const char* ptr) {
this->data = ptr;
}
MyAwesomeClass(MyAwesomeClass&& myAwesomeClassRVALUE) {
this->data = myAwesomeClassRVALUE.data;
myAwesomeClassRVALUE.data = nullptr;
}
~MyAwesomeClass() {
delete data;
}
};
int main() {
MyAwesomeClass s = "My Awesome Class' string data.";
return 0;
}
Usually temporary objects get destructed automatically but what happens with pointers created in this way?
Pointers are objects themselves. Same thing happens to temporary pointer objects as happens to all other temporary objects.
Your example deletes a pointer pointing to a string literal, so the behaviour of the program is undefined. The class would also have a broken assignement operators even if it was used correctly by passing pointer to an array allocated with new[].
As for rvalueness, the only rvalue expression in the example that I could find is nullptr.
Note this answer is based on a previous edit of the question where MyAwesomeClass was a String class.
Your String class is not really a string class as it doesn't own the underlying string data. It's more akin to std::string_view.
You have two avenues I can see you can pursue:
Your class owns the underlying data. In this case the class is a wrapper around std::string and has std::stringdata member. Look up composition. No need to worry about lifetime asstd::string` is well behaved.
Your class is like a "pointer/reference/view" to another string. In this case you have a const char* and maybe std::size_t size data member. You basically have a std::string_view (except for the wisdom, expertise and experience that went into designing std::string_view). Since you don't own the underlying data you can't do anything about the lifetime of the underlying data. The user of the class must make sure it doesn't end with a "YourStringView" to an expired object, just as he/she needs to make sure it doesn't end up with a reference/pointer to an expired object.
The semantics of these two scenarios are wildly different (as the difference between an object and a pointer to an object).
Anyway I wouldn't recommend you do any of this except for maybe learning reasons. std::string_view already exists so just use that. If you want the printing capabilities use the fmt library or the C++ format library (that is based on the mentioned fmt library).
Even if you decide to do this for learning purposes I highly encourage you look into these alternatives and learn from how they are doing things.
MyAwesomeClass(const char* ptr)
{
this->data = ptr;
}
~MyAwesomeClass()
{
delete data;
}
Oh no, no, no! No!!
Please look into RAII and rule of 0/3/5. Your class either owns the pointed object or it doesn't. If it owns it then it is responsible for creating it and deleting it. If it doesn't then it can't do either. You can't have "half of responsibilities" where you are responsible for deleting it but not for creating it.
In user code you should never need to manually manage memory. Use the rule of 0.
Quick answer is that your class does not own the data, but just the raw pointer. Under certain conditions you will see a problem with delete operator. Raw pointer are not great tool to ensure correct object ownership.
is it possible to replace an object in a std::vector with another object of same type and maintain any references to this object which was just replaced?
Let's say we have a
std::vector<MyObj> objs
and I want to replace an MyObj-object such that all references to this MyObj are still intact - is this possible?
You cannot do it in general without some serious misuse of the language (e.g. using placement new in a situation for which it has not been designed). However, you can design MyObj class in a way that makes implementing this use case very straightforward. For example, if MyObj follows the Envelope and Letter Idiom which puts the implementation (the letter) and its wrapper (the envelope) into separate classes, you could make an array of wrappers, and replace their inner content without disturbing outside references:
// The real functionality goes here
struct MyObjImpl {
void operation1() { ... }
void operation2() { ... }
};
// This is a wrapper that forwards calls to the implementation,
// which can be switched at any time without disturbing references to MyObj
class MyObj {
MyObjImpl* wrapped;
public:
MyObj(MyObjImpl* rep) : wrapped(rep) {}
void switch(MyObjectImpl* rep) {
wrapped = rep;
}
void operation1() { wrapped->operation1(); }
void operation2() { wrapped->operation2(); }
};
Assuming that the only way to "replace" an object is to assign to it, then it is in fact unlikely that any references to the assigned-to object are invalidated in the first place.
That is, the answer is: You do not have to do anything special. All references to the "replaced" object remain intact.
Of course, there are other reasons that references to an object held in a std::vector are invalidated, but they do not happen during a well-behaved assignment to the object being held.
Maybe you will want to save states of replaced objects via memento pattern as it's called or just a save state control object. Ref to memento from wiki https://en.wikipedia.org/wiki/Memento_pattern as it's lacking C++ implementation it's simple to write. Also you may want to keep track of replaced objects so might extend it a bit.
I have a map of that maps string ids to a specific implementation of base_object.
Any base_object has a method get_id that returns the id of the object.
and I fill up the map using (pseudo)
void addToMap(base_object* obj){
make_pair(obj->get_id(),obj);
}
This map acts like a factory, and knows to return the correct object using the id. this is the method declaration for retrieving specific object:
base_object* get(string id);
All the objects do not have fields only a polymorphic method that behaves differently for each implementation.
I am not sure what is the best way to implement this.
Should the map be a map of <string,unique_ptr<base_object>>
In this case when I am returning a base_object using get, is it ok to return a raw pointer to base_object? (I know that the map will keep living so that the object will not get destroyed?)
or maybe in this case I should use a shared_ptr?
Also, since the object doesn't really have any fields, maybe it is better to return a copy of the object?
Any way I look at this it looks to me like a bad design, and I just can't decide what is the best approach to solve this.
I am very new to cpp so I am not very familiar with all the differences and best usages of pointers...
Use unique_ptr<base_object> const &. That signals to the caller that what it gets is a handle on a unique object having the id that it requested. Using a shared_ptr signals that it may be responsible for keeping the object alive.
Also, there's no need for a map: you can use a set or unordered_set that orders/hashes based on the id. That way, you won't have to store the id twice.
(The thing you're implementing is more of an object pool than a factory.)
You can use std::unique_ptr<base_object> and return a const reference to the unique_ptr.
Possible implementation:
struct Data
{
std::map<std::string,std::unique_ptr<base_object>> data;
void add(base_object* obj){
data[obj->get_id()] = std::unique_ptr<base_object>(obj);
}
const std::unique_ptr<base_object>& get(const std::string& id) {
return data.at(id);
}
};
Use case example:
Data data;
data.add(new test1_object{});
data["test1"]->create(); // call a virtual function of base_object
Note, that this is not really a factory. If the abstract function of base_object should be responsible for creating your actual product, you can perhaps do this:
struct Factory
{
std::map<std::string,std::unique_ptr<base_object>> workers;
void add(base_object* obj){
data[obj->get_id()] = std::unique_ptr<base_object>(obj);
}
Product create(const std::string& id) {
return data.at(id)->foo(); // call the virtual function here
}
};
Factory factory;
factory.add(new test1_object{});
Product x = factory.create("test1");
The standard of the factory pattern is not filled up with objects on start. There are no objects at all. The factory only knows how to create a new object. A possible implementation can do this with a map and registered static! methods of the class ( not a object ).
And a factory should always return a new instance and not a reference or a pointer to an already existent object. The application typically have no idea how to destroy this special kind of copies instead of own instances.
How you best design your thing depends on the use-cases and design constraints:
If you can guarantee the returned object lives long enough and you can guarantee you'll return an actual object, returning by reference is best (if not having an object to return, there are exceptions. Use where appropriate).
If you can guarantee the returned object lives long enough if there is an object to return, just return by raw pointer.
If all of the maps elements should live to the end of the program, do not make the map own them unless it lives as long and you want to force it to clean them up: Allocate them statically or dynamically (in the second case you might want to register them with std::atexitfor cleanup at the end), and let the map only hold an un-owning raw pointer.
If your program will not be compiled without RTTI and every object is a singleton of its class, consider using typeid for the key (at least the name field) instead of a custom string.
Anyway, you later say the only interesting point of those objects is one single method: Why don't you just use std::function for maximum flexibility?
A std::unordered_map has amortised O(1) access, a std::map only O(ln(n)).
I'm looking for a way to ensure that an object that is executed on the heap is ALWAYS deallocated when I'm done with it.
I know that if it's allocated on the stack, I can use RAII to ensure it will be taken care of - unfortunately, this won't work for me (at least directly) because the object in question is actually created by calling an api function, which then returns a pointer to the object it creates on the heap.
So, conceptually, what I want to do is something like:
TheApi::ApiObject* p_myObj = TheApi::createAnApiObj();
try
{
doStuffWithMyObjThatMayError(p_myObj);
}
finally
{
delete p_myObj;
}
The only thing I can think of to do would be to make some sort of dummy cleanup class, and create an instance of that on the stack:
class MegaMaid
{
private:
TheApi::ApiObject* toFree;
public:
MegaMaid(TheApi::ApiObject* toFree)
{
this->toFree = toFree;
}
~MegaMaid()
{
delete toFree;
}
};
void doStuff()
{
TheApi::ApiObject* p_myObj = TheApi::createAnApiObj();
TheApi::ApiObject* p_myObj;
MegaMaid cleaner(p_myObj);
doStuffWithMyObjThatMayError(p_myObj);
}
Is there a better way to accomplish this? Or is this the accepted solution?
You can still use RAII on pointers returned by functions. You can use smart pointers (which is exactly what the dummy class you are describing is) like this:
std::unique_ptr<TheApi::ApiObject> p_myObj(TheApi::createAnApiObj());
That "dummy class" is known as "smart pointer". You should check out std::auto_ptr or boost::shared_ptr. They provide exactly what you look for.
I have objects which create other child objects within their constructors, passing 'this' so the child can save a pointer back to its parent. I use boost::shared_ptr extensively in my programming as a safer alternative to std::auto_ptr or raw pointers. So the child would have code such as shared_ptr<Parent>, and boost provides the shared_from_this() method which the parent can give to the child.
My problem is that shared_from_this() cannot be used in a constructor, which isn't really a crime because 'this' should not be used in a constructor anyways unless you know what you're doing and don't mind the limitations.
Google's C++ Style Guide states that constructors should merely set member variables to their initial values. Any complex initialization should go in an explicit Init() method. This solves the 'this-in-constructor' problem as well as a few others as well.
What bothers me is that people using your code now must remember to call Init() every time they construct one of your objects. The only way I can think of to enforce this is by having an assertion that Init() has already been called at the top of every member function, but this is tedious to write and cumbersome to execute.
Are there any idioms out there that solve this problem at any step along the way?
Use a factory method to 2-phase construct & initialize your class, and then make the ctor & Init() function private. Then there's no way to create your object incorrectly. Just remember to keep the destructor public and to use a smart pointer:
#include <memory>
class BigObject
{
public:
static std::tr1::shared_ptr<BigObject> Create(int someParam)
{
std::tr1::shared_ptr<BigObject> ret(new BigObject(someParam));
ret->Init();
return ret;
}
private:
bool Init()
{
// do something to init
return true;
}
BigObject(int para)
{
}
BigObject() {}
};
int main()
{
std::tr1::shared_ptr<BigObject> obj = BigObject::Create(42);
return 0;
}
EDIT:
If you want to object to live on the stack, you can use a variant of the above pattern. As written this will create a temporary and use the copy ctor:
#include <memory>
class StackObject
{
public:
StackObject(const StackObject& rhs)
: n_(rhs.n_)
{
}
static StackObject Create(int val)
{
StackObject ret(val);
ret.Init();
return ret;
}
private:
int n_;
StackObject(int n = 0) : n_(n) {};
bool Init() { return true; }
};
int main()
{
StackObject sObj = StackObject::Create(42);
return 0;
}
Google's C++ programming guidelines have been criticized here and elsewhere again and again. And rightly so.
I use two-phase initialization only ever if it's hidden behind a wrapping class. If manually calling initialization functions would work, we'd still be programming in C and C++ with its constructors would never have been invented.
Depending on the situation, this may be a case where shared pointers don't add anything. They should be used anytime lifetime management is an issue. If the child objects lifetime is guaranteed to be shorter than that of the parent, I don't see a problem with using raw pointers. For instance, if the parent creates and deletes the child objects (and no one else does), there is no question over who should delete the child objects.
KeithB has a really good point that I would like to extend (in a sense that is not related to the question, but that will not fit in a comment):
In the specific case of the relation of an object with its subobjects the lifetimes are guaranteed: the parent object will always outlive the child object. In this case the child (member) object does not share the ownership of the parent (containing) object, and a shared_ptr should not be used. It should not be used for semantic reasons (no shared ownership at all) nor for practical reasons: you can introduce all sorts of problems: memory leaks and incorrect deletions.
To ease discussion I will use P to refer to the parent object and C to refer to the child or contained object.
If P lifetime is externally handled with a shared_ptr, then adding another shared_ptr in C to refer to P will have the effect of creating a cycle. Once you have a cycle in memory managed by reference counting you most probably have a memory leak: when the last external shared_ptr that refers to P goes out of scope, the pointer in C is still alive, so the reference count for P does not reach 0 and the object is not released, even if it is no longer accessible.
If P is handled by a different pointer then when the pointer gets deleted it will call the P destructor, that will cascade into calling the C destructor. The reference count for P in the shared_ptr that C has will reach 0 and it will trigger a double deletion.
If P has automatic storage duration, when it's destructor gets called (the object goes out of scope or the containing object destructor is called) then the shared_ptr will trigger the deletion of a block of memory that was not new-ed.
The common solution is breaking cycles with weak_ptrs, so that the child object would not keep a shared_ptr to the parent, but rather a weak_ptr. At this stage the problem is the same: to create a weak_ptr the object must already be managed by a shared_ptr, which during construction cannot happen.
Consider using either a raw pointer (handling ownership of a resource through a pointer is unsafe, but here ownership is handled externally so that is not an issue) or even a reference (which also is telling other programmers that you trust the referred object P to outlive the referring object C)
A object that requires complex construction sounds like a job for a factory.
Define an interface or an abstract class, one that cannot be constructed, plus a free-function that, possibly with parameters, returns a pointer to the interface, but behinds the scenes takes care of the complexity.
You have to think of design in terms of what the end user of your class has to do.
Do you really need to use the shared_ptr in this case? Can the child just have a pointer? After all, it's the child object, so it's owned by the parent, so couldn't it just have a normal pointer to it's parent?