C++: make full copy of vector of pointers - c++

I'm working on a C++ game. My level objects are in a vector (Object being a superclass for my level's objects).
I need the state of this vector to be saved at checkpoints, and retrieved at death.
So at the beginning of the level, the vector (objects) is created (old_objects).
If you hit a checkpoint, old_objects is erased and objects is re-copied to old_objects.
If you die, the data from objects is erased and old_objects is copied back to objects.
I've been trying to do this several ways but I'm not able to get it working. Help?
EDIT: I tried using a virtual clone() method. It throws out of range errors.
class Object {
public:
virtual Object* clone() { return new Object(); }
};
class SubObjectA {
public:
Object* clone() { return new SubObjectA(datablahblah); }
};
class SubObjectB {
public:
Object* clone() { return new SubObjectB(datablahblah); }
};
for (vector<Object*>::iterator it = objects.begin(); it != objects.end(); it++) {
Object* tempobj = *it;
old_objects.push_back(tempobj->clone());
}
But all I get is the same old:
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

You could use the Prototype pattern and have your Object base class declare a pure virtual clone() method. Then at checkpoint time you just have to iterate over the vector calling clone on the pointers and pushing them into the new vector.

The only important requirement here is to provide a deep copy constructor that stores all necessary information (or if that's not possible, a method to get all the required info). Then use that ctor/method to create a second vector. Something like:
class Object
{
RenderObject * m_Renderable;
int m_Health;
float3 m_Position;
Object(const Object * other) :
m_Renderable(nullptr),
m_Health(other->m_Health),
m_Position(other->m_Position)
{ };
Object * GetStorable()
{
return new Object(*this);
}
};
then to store "checkpoints", you simply do:
vector<vector<shared_ptr<Object>>> gCheckpoints;
vector<shared_ptr<Object>> gLevelObjects;
vector<shared_ptr<Object>> checkpoint;
std::for_each(
gLevelObjects.begin(), gLevelObjects.end(),
[&](shared_ptr<Object> obj)
{
checkpoint.push_back(obj->GetStorable());
});
gCheckpoints.push_back(checkpoint); // creates a new checkpoint
You will need to recreate the render information for the objects when the checkpoint is restored; this is a requirement for most save systems in most graphics contexts, however.
Depending on how your Object class is set up, you can also inherit from another class that consists only of the stored data, and simply store that and create the renderables from it when necessary (class RenderObject : StoredObject { ... };).
You can alternatively serialize the objects in some fashion (binary save, xml, json) and store that to a file (autosave/quicksave/checkpoint) or in-memory, and then use your regular loading mechanism to load that specific file.
The best method depends on what your plans are and how your system is set up, but this concept should provide the basics or give you a starting point.

Related

Going up the object hierarchy

Hi so I've got some nice tree hierarchy of objects in program i'm working on. I've came across a problem with communicating the bottom to top way. How I have it set up right now is that in every constructor I pass a reference to object creating the new object. Simple structure would look like this:
[Controller] -> [World] -> [Object]
Going up one layer (from world to controller or from object to world) is OK. But where the problem starts to occur is when I try to go up 2 layers.
Here is a simplified structure of how I have set it up:
Controller.h:
#include "World.h"
Class Controller {
public:
Controller() {
_worlds.push_back(World(*this));
)
void update() { // Called on a loop from main program loop
_worlds[0].update(); // Calls update of active world, in this case world[0]
}
vector<World> _worlds;
Camera _camera; // class with checkIfInView function
}
World.h:
#Include "Object.h"
Class Controller;
Class World {
World(Controller& ref) : _controller(ref) {
_objects.push_back(Object(*this));
_controller._camera.doStuff(); // works OK
}
void update() {
for (auto& i : _objects)
i.update();
}
vector<Object> _objects;
Controller& _controller;
}
Object.h:
Class World;
Class Object {
Object(World& ref) : _world(ref) {}
void update();
World& _world;
}
Object.cpp:
#include "Controller.h"
#include "World.h"
void Object::update() {
_world._controller._camera.checkIfInView(*this); // Read access violation
}
Controller hold one single camera object which is responsible for what is being shown. What I need is a way for Objects to call checkIfInView to know if they should render or not. Is there any other way to do this or a way to fix it?
EDIT: Updated code.
The problem
Let's look at your nice chain, starting with the Controller constructor. As it's the top object of your hierarchy, it the start of the construction. I imagine that in main() you have something like
Controller c;
This will cause the constructor to be called:
Controller() {
_worlds.push_back(World(*this)); // !!!
}
World(*this) will create a new temporary world that you'll push into the vector of worlds of your controller. The temporary object only exists for the time of the expression in which it appears.
The temporary World will then be constructed with
World(Controller& ref) : _controller(ref) { // ref to controller is kept
_objects.push_back(Object(*this)); // ouch!!!
_controller._camera.doStuff(); // works OK
}
Now an object will be created which refers to *this world. Ouch!! Remember that that world is temporary ? At the end of the construction it will be deleted, so that all objects will refer to a C++ object that no longer exists and hence the UB which happen to produce the segmentation fault in your case.
The start of a solution
The design that you have is quite delicate. Think twice if you couldn't find a safer design pattern. If you want nevertheless to pursue in this direction, avoid creating objects using temporary items: create dynamically allocated ones instead.
Controller() {
_worlds.push_back(*new World(*this)); // !!! risk of leakage
}
The next thing would be to use pointers instead of references:
Controller() {
_worlds.push_back(new World(*this)); // risk of leakage
}
Of course, you'd need to change the rest of the code accordingly, to work with pointers.
The next thing would be to opt for shared pointers: this avoids risk of leakage:
Controller() {
_worlds.push_back(make_shared<World>(*this)); // risk of leakage
}
In the adaptation of your code you'd then need to make a difference between shared_ptr in your vectors, which refers to the object, and weak_ptr to the parten objects, to indicate tha the parent is now shared owned by the child but by another object.
A better solution ?
I warn you that it will not be a piece of cake. As soon as you have pointers, you'd need to take care of the rule of 3 for each class.
Many issues arise from:
1) the nested construction -> may be worth considering the builder design pattern
2) the risk of mixing of static objects and dynamically created objects, never knowing which kind is the parent. -> may be worth using a protected/private constructor and use a factory method for making sure that all objects are always dynamic objects.

Cast Object at Runtime Depending on Instance Variable (C++)

I'm trying to represent a 2 dimensional map of objects. So I have a two-dimensional array of "MapItems":
MapItem* world_map[10][10];
In my specific situation, these MapItems are going to be used to represent Drones, Static Objects (like trees or any obstruction that doesn't move), or empty positions (these objects will be subclasses of MapItem):
class Drone : public MapItem {
int droneId;
...
}
class StaticObject : public MapItem {
...
}
class EmptyPosition : public MapItem {
int amount_of_time_unoccupied;
...
}
Is it a good idea to have an instance variable on the MapItem class that tells what specific type of item it is, and then cast it the proper type based on that? For example:
enum ItemType = {DRONE, STATIC_OBSTRUCTION, EMPTY};
class MapItem {
ItemType type;
...
}
And then when I want to know what is at a position in the map, I do:
MapItem *item = world_map[3][3];
if (item->type == DRONE) {
Drone *drone = dynamic_cast<Drone*>(item);
// Now do drone specific things with drone
...
} else if (item->type == STATIC_OBSTRUCTION) {
StaticObject *object = dynamic_case<StaticObject*>(item);
// Static object specific stuff
...
} else {
...
}
I have not actually tried this, but I assume it's possible. What I'm really asking is this a good design pattern? Or is there a better way to do this?
A "switch on type" indicates a design problem much more often than not.
What you usually want to do is define and implement some virtual functions for the behaviors you care about. For example, you might care about flying into one of the spaces. If so, you might have a function to see if it allows entry. That will return true if a drone is trying fly into open air, or false if it's trying to fly into a tree.
As an aside, if you're going to have derived objects, you need to define the array as container pointers, not actual objects of the base class. Otherwise, when you try to put a derived object into the array, it'll get "sliced" to become an object of the base class.

Why do they say that in the Proto-type Pattern - be used to simply duplicate the original object whenever a new one is needed?

I am trying to learn the design pattern. I am a C++ programmer. Currently, I am juggling with the Proto-type pattern. I could co-relate Prototype with the factory type. However, there are a lot of differences between factory and prototype pattern. For example, in the prototype pattern each derived class registers its prototype with the base/super class.
However, looking at the wikipedia article - I couldn't understood the following points.
Rather than retrieving the data and re-parsing it each time a new object is created, the prototype pattern can be used to simply duplicate the original object whenever a new one is needed.
avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) when it is prohibitively expensive for a given application.
Here is the program, I created to demonstrate the prototype pattern in C++. However, I cannot find any benefit out of it. How come a prototype pattern will help in quickly creating the object here. I can see that the object has to call 'new' every time. Here is the entire program, please correct me if you think that I haven't implemented the prototype pattern correctly.
Sorry for the long program - but trust me it is quite simple.
Like a factory object - here is the prototype class
-- basically an abstract.
class Itransport
{
public:
enum transportPacketType
{
udp,
tcp,
MAX
};
private:
static std::list<Itransport *> prototypesList;
protected:
virtual Itransport::transportPacketType getPacketType() = 0;
virtual Itransport* clone() = 0;
/** This will be called by the derived classes **/
static void registertoPrototypeList(Itransport *packet)
{
prototypesList.push_back(packet);
}
public:
virtual void showMessage() = 0;
static Itransport* makeClone(Itransport::transportPacketType packType)
{
std::list<Itransport *>::iterator it;
for(it = prototypesList.begin(); it != prototypesList.end(); it++)
{
if( (*it)->getPacketType() == packType )
{
return (*it)->clone();
}
}
}
virtual ~Itransport() = 0;
};
Itransport::~Itransport()
{
std::cout<<"Itransport Destructor called"<<std::endl;
}
std::list<Itransport *> Itransport::prototypesList;
Here is the concrete type of the Itransport Packet -
class udpPacket: public Itransport
{
private:
static udpPacket udpTransportPacket;
protected:
Itransport::transportPacketType getPacketType()
{
return Itransport::udp;
}
Itransport* clone()
{
return new udpPacket();
}
public:
void showMessage()
{
std::cout<<"This is a UDP Packet"<<std::endl;
}
udpPacket()
{
std::cout<<"UDP Packet Constructed"<<std::endl;
registertoPrototypeList(this);
}
~udpPacket()
{
std::cout<<"Destructor of udp called"<<std::endl;
}
};
static udpPacket udpTransportPacket;
Here is the client -
int main()
{
Itransport *udpPacket;
Itransport *udpPacket2;
udpPacket = Itransport::makeClone(Itransport::udp);
udpPacket->showMessage();
udpPacket2 = Itransport::makeClone(Itransport::udp);
udpPacket2->showMessage();
delete udpPacket;
delete udpPacket2;
return 0;
}
I couldn't find any benefits related to 'new' here. Please throw some light on it.
I can have a go at explaining the first point:
Rather than retrieving the data and re-parsing it each time a new
object is created, the prototype pattern can be used to simply
duplicate the original object whenever a new one is needed.
Imagine a computer game that has to create a lot of monsters. Say all the different types of monster are not known at compile time but you construct a monster of a particular type from some input data that provides information about what color the monster is, etc:
class Monster {
public:
Monster(InputDataHandle handle) {
// Retrieve input data...
// Parse input data...
}
void setPosition(Position);
};
Then every time you want to construct, say a red monster you have to retrieve the data and re-parse:
// Spawn a lot of red monsters
for (int i = 0; i != large_number; ++i) {
auto red = new Monster(red_monster_data); // Must retrieve data and re-parse!
red->setPosition(getRandomPosition());
game.add(red);
}
Clearly that is inefficient. One way of solving it is using the Prototype Pattern. You create one "prototype" red monster and every time you want to create an instance of a red monster you simply copy the prototype and you don't have to retrieve and re-parse the input data:
auto prototype_red_monster = new Monster(red_monster_data);
for (int i = 0; i != large_number; ++i) {
auto red = prototype_red_monster->clone();
red->setPosition(getRandomPosition());
game.add(red);
}
But how is the clone function implemented? This brings us to the second point which I don't really understand:
avoid the inherent cost of creating a new object in the standard way
(e.g., using the 'new' keyword) when it is prohibitively expensive for
a given application.
The clone function fundamentally has to allocate memory for the new object and copy data in from itself. I'm not sure I know what they are referring to when they talk about the "inherent cost of the new keyword". The examples are in Java and C# which have clone() and MemberwiseClone() respectively. In those languages you don't need to call new. I don't know how clone() and MemberwiseClone() are implemented but I don't see how they can "avoid the inherent cost of the new keyword".
In C++ we have to implement clone() ourselves and it will typically use new and use the copy constructor:
Monster* clone() {
return new Monster(*this);
}
In this case the copy constructor is much cheaper than creating the object from scratch. In your case it might not be.
The fact you cannot find any benefit from the Prototype Pattern in your case might mean it is the wrong pattern for your case and you will be better off with a different pattern like the Object Pool, Flyweight or Abstract Factory Pattern.

Creating a new object by calling the new constructor with a string

I was recently in a job interview and my interviewer gave me a modeling question that involved serialization of different shapes into a file.
The task was to implements shapes like circle or rectangles by first defining an abstract class named Shape and then implements the various shapes (circle, rectangle..) by inheriting from the base class (Shape).
The two abstract methods for each shape were: read_to_file (which was supposed to read the shape from a file) and write_to_file which supposed to write the shape into a file.
All was done by the implementation of that virtual function in the inherited shape (Example: For Circle I was writing the radius, for square I saved the side of the square....).
class Shape {
public:
string Shape_type;
virtual void write_into_file()=0;
virtual void read_into_files()=0;
Shape() {
}
virtual ~Shape() {
}};
class Square: public Shape {
public:
int size;
Square(int size) {
this->size = size;
}
void write_into_file() {
//write this Square into a file
}
void read_into_files() {
//read this Square into a file
}
};
That was done in order to see if I know polymorphism.
But, then I was asked to implement two functions that take a vector of *shape and write/read it into a file.
The writing part was easy and goes something like that:
for (Shape sh : Shapes) {
s.write_into_file();
}
as for the reading part I thought about reading the first word in the text (I implemented the serializable file like a text file that have this line: Shape_type: Circle, Radius: 12; Shape_type:Square...., so the first words said the shape type). and saving it to a string such as:
string shape_type;
shape_type="Circle";
Then I needed to create a new instance of that specific shape and I thought about something like a big switch
<pre><code>
switch(shape_type):
{
case Circle: return new circle;
case Square: return new square
......
}
</pre></code>
And then, the interviewer told me that there is a problem with this implementation
which I thought was the fact that every new shape the we will add in the future we should also update int that big swicht. he try to direct me into a design pattern, I told him that maybe the factory design pattern will help but I couldn't find a way to get rid of that switch. even if I will move the switch from the function into a FactoryClass I will still have to use the switch in order to check the type of the shape (according to the string content i got from the text file).
I had a string that I read from the file, that say the current type of the shape. I wanted to do something like:
string shape_type;
shape_type="Circle";
Shape s = new shape_type; //which will be like: Shape s = new Circle
But I can't do it in c++.
Any idea on what I should have done?
In you factory you could map a std::string to a function<Shape*()>. At startup you register factory methods will the factory:
shapeFactory.add("circle", []{new Circle;});
shapeFactory.add("square", []{new Square;});
shapeFactory.add("triangle", []{new Triangle;});
In your deserialization code you read the name of the type and get its factory method from the factory:
std::string className = // read string from serialization stream
auto factory = shapeFactory.get(className);
Shape *shape = factory();
You've now got a pointer to the concrete shape instance which can be used to deserialize the object.
EDIT: Added more code as requested:
class ShapeFactory
{
private:
std::map<std::string, std::function<Shape*()> > m_Functions;
public:
void add(const std::string &name, std::function<Share*()> creator)
{
m_Functions.insert(name, creator)
}
std::function<Shape*()> get(const std::string &name) const
{
return m_Functions.at(name);
}
};
NOTE: I've left out error checking.
In C++, with
for (Shape sh : Shapes) {
s.write_into_file();
}
you have object slicing. The object sh is a Shape and nothing else, it looses all inheritance information.
You either need to store references (not possible to store in a standard collection) or pointers, and use that when looping.
In C++ you would to read and write some kind of type tag into the file to remember the concrete type.
A virtual method like ShapeType get_type_tag() would do it, where the return type is an enumeration corresponding to one of the concrete classes.
Thinking about it, though, the question was probably just getting at wanting you to add read and write functions to the interface.
You could create a dictionary of factory functions keyed by a shape name or shape id (shape_type).
// prefer std::shared_ptr or std::unique_ptr of course
std::map<std::string, std::function<Shape *()>> Shape_Factory_Map;
// some kind of type registration is now needed
// to build the map of functions
RegisterShape(std::string, std::function<Shape *()>);
// or some kind of
BuildShapeFactoryMap();
// then instead of your switch you would simply
//call the appropriate function in the map
Shape * myShape = Shape_Factory_Map[shape_type]();
In this case though you still have to update the creation of the map with any new shapes you come up with later, so I can't say for sure that it buys you all that much.
All the answers so far still appear to have to use a switch or map somewhere to know which class to use to create the different types of shapes. If you need to add another type, you would have to modify the code and recompile.
Perhaps using the Chain of Responsibility Pattern is a better approach. This way you can dynamically add new creation techniques or add them at compile time without modifying any already existing code:
Your chain will keep a linked list of all the creation types and will traverse the list until it finds the instance that can make the specified type.
class Creator{
Creator*next; // 1. "next" pointer in the base class
public:
Creator()
{
next = 0;
}
void setNext(Creator*n)
{
next = n;
}
void add(Creator*n)
{
if (next)
next->add(n);
else
next = n;
}
// 2. The "chain" method in the Creator class always delegates to the next obj
virtual Shape handle(string type)
{
next->handle(i);
}
);
Each subclass of Creator will check if it can make the type and return it if it can, or delegate to the next in the chain.
I did create a Factory in C++ some time ago in which a class automatically registers itself at compile time when it extends a given template.
Available here: https://gist.github.com/sacko87/3359911.
I am not too sure how people react to links outside of SO but it is a couple of files worth. However once the work is done, using the example within that link, all that you need to do to have a new object included into the factory would be to extend the BaseImpl class and have a static string "Name" field (see main.cpp). The template then registers the string and type into the map automatically. Allowing you to call:
Base *base = BaseFactory::Create("Circle");
You can of course replace Base for Shape.

Porting an existing class structure to smart pointers

I know this question is rather long, but I was not sure how to explain my problem in a shorter way. The question itself is about class hierarchy design and, especially, how to port an existing hierarchy based on pointers to one using smart pointers. If anyone can come up with some way to simplify my explanation and, thus, make this question more generic, please let me know. In that way, it might be useful for more SO readers.
I am designing a C++ application for handling a system that allows me to read some sensors. The system is composed of remotes machines from where I collect the measurements. This application must actually work with two different subsystems:
Aggregated system: this type of system contains several components from where I collect measurements. All the communication goes through the aggregated system which will redirect the data to the specific component if needed (global commands sent to the aggregated system itself do not need to be transferred to individual components).
Standalone system: in this case there is just a single system and all the communication (including global commands) is sent to that system.
Next you can see the class diagram I came up with:
The standalone system inherits both from ConnMgr and MeasurementDevice. On the other hand, an aggregated system splits its functionality between AggrSystem and Component.
Basically, as a user what I want to have is a MeasurementDevice object and transparently send data to corresponding endpoint, be it an aggregated system or a standalone one.
CURRENT IMPLEMENTATION
This is my current implementation. First, the two base abstract classes:
class MeasurementDevice {
public:
virtual ~MeasurementDevice() {}
virtual void send_data(const std::vector<char>& data) = 0;
};
class ConnMgr {
public:
ConnMgr(const std::string& addr) : addr_(addr) {}
virtual ~ConnMgr() {}
virtual void connect() = 0;
virtual void disconnect() = 0;
protected:
std::string addr_;
};
These are the classes for an aggregated system:
class Component : public MeasurementDevice {
public:
Component(AggrSystem& as, int slot) : aggr_sys_(as), slot_(slot) {}
void send_data(const std::vector<char>& data) {
aggr_sys_.send_data(slot_, data);
}
private:
AggrSystem& aggr_sys_;
int slot_;
};
class AggrSystem : public ConnMgr {
public:
AggrSystem(const std::string& addr) : ConnMgr(addr) {}
~AggrSystem() { for (auto& entry : components_) delete entry.second; }
// overridden virtual functions omitted (not using smart pointers)
MeasurementDevice* get_measurement_device(int slot) {
if (!is_slot_used(slot)) throw std::runtime_error("Empty slot");
return components_.find(slot)->second;
}
private:
std::map<int, Component*> components_;
bool is_slot_used(int slot) const {
return components_.find(slot) != components_.end();
}
void add_component(int slot) {
if (is_slot_used(slot)) throw std::runtime_error("Slot already used");
components_.insert(std::make_pair(slot, new Component(*this, slot)));
}
};
This is the code for a standalone system:
class StandAloneSystem : public ConnMgr, public MeasurementDevice {
public:
StandAloneSystem(const std::string& addr) : ConnMgr(addr) {}
// overridden virtual functions omitted (not using smart pointers)
MeasurementDevice* get_measurement_device() {
return this;
}
};
These are factory-like functions responsible for creating ConnMgr and MeasurementDevice objects:
typedef std::map<std::string, boost::any> Config;
ConnMgr* create_conn_mgr(const Config& cfg) {
const std::string& type =
boost::any_cast<std::string>(cfg.find("type")->second);
const std::string& addr =
boost::any_cast<std::string>(cfg.find("addr")->second);
ConnMgr* ep;
if (type == "aggregated") ep = new AggrSystem(addr);
else if (type == "standalone") ep = new StandAloneSystem(addr);
else throw std::runtime_error("Unknown type");
return ep;
}
MeasurementDevice* get_measurement_device(ConnMgr* ep, const Config& cfg) {
const std::string& type =
boost::any_cast<std::string>(cfg.find("type")->second);
if (type == "aggregated") {
int slot = boost::any_cast<int>(cfg.find("slot")->second);
AggrSystem* aggr_sys = dynamic_cast<AggrSystem*>(ep);
return aggr_sys->get_measurement_device(slot);
}
else if (type == "standalone") return dynamic_cast<StandAloneSystem*>(ep);
else throw std::runtime_error("Unknown type");
}
And finally here it is main(), showing a very simple usage case:
#define USE_AGGR
int main() {
Config config = {
{ "addr", boost::any(std::string("192.168.1.10")) },
#ifdef USE_AGGR
{ "type", boost::any(std::string("aggregated")) },
{ "slot", boost::any(1) },
#else
{ "type", boost::any(std::string("standalone")) },
#endif
};
ConnMgr* ep = create_conn_mgr(config);
ep->connect();
MeasurementDevice* dev = get_measurement_device(ep, config);
std::vector<char> data; // in real life data should contain something
dev->send_data(data);
ep->disconnect();
delete ep;
return 0;
}
PROPOSED CHANGES
First of all, I wonder whether there is a way to avoid the dynamic_cast in get_measurement_device. Since AggrSystem::get_measurement_device(int slot) and StandAloneSystem::get_measurement_device() have different signatures, it is not possible to create a common virtual method in the base class. I was thinking to add a common method accepting a map containing the options (e.g., the slot). In that case, I would not need to do the dynamic casting. Is this second approach preferable in terms of a cleaner design?
In order to port the class hierarchy to smart pointers I used unique_ptr. First I changed the map of components in AggrSystem to:
std::map<int, std::unique_ptr<Component> > components_;
The addition of a new Component now looks like:
void AggrSystem::add_component(int slot) {
if (is_slot_used(slot)) throw std::runtime_error("Slot already used");
components_.insert(std::make_pair(slot,
std::unique_ptr<Component>(new Component(*this, slot))));
}
For returning a Component I decided to return a raw pointer since the lifetime of a Component object is defined by the lifetime of an AggrSystem object:
MeasurementDevice* AggrSystem::get_measurement_device(int slot) {
if (!is_slot_used(slot)) throw std::runtime_error("Empty slot");
return components_.find(slot)->second.get();
}
Is returning a raw pointer a correct decision? If I use a shared_ptr, however, then I run into problems with the implementation for the standalone system:
MeasurementDevice* StandAloneSystem::get_measurement_device() {
return this;
}
In this case I cannot return a shared_ptr using this. I guess I could create one extra level of indirection and have something like StandAloneConnMgr and StandAloneMeasurementDevice, where the first class would hold a shared_ptr to an instance of the second.
So, overall, I wanted to ask whether this a good approach when using smart pointers. Would it be preferable to use a map of shared_ptr and return a shared_ptr too, or is it better the current approach based on using unique_ptr for ownership and raw pointer for accessing?
P.S: create_conn_mgr and main are changed as well so that instead of using a raw pointer (ConnMgr*) now I use unique_ptr<ConnMgr>. I did not add the code since the question was already long enough.
First of all, I wonder whether there is a way to avoid the
dynamic_cast in get_measurement_device.
I would attempt to unify the get_measurement_device signatures so that you can make this a virtual function in the base class.
So, overall, I wanted to ask whether this a good approach when using
smart pointers.
I think you've done a good job. You've basically converted your "single ownership" news and deletes to unique_ptr in a fairly mechanical fashion. This is exactly the right first (and perhaps last) step.
I also think you made the right decision in returning raw pointers from get_measurement_device because in your original code the clients of this function did not take ownership of this pointer. Dealing with raw pointers when you do not intend to share or transfer ownership is a good pattern that most programmers will recognize.
In summary, you've correctly translated your existing design to use smart pointers without changing the semantics of your design.
From here if you want to study the possibility of changing your design to one involving shared ownership, that is a perfectly valid next step. My own preference is to prefer unique ownership designs until a use case or circumstance demands shared ownership.
Unique ownership is not only more efficient, it is also easier to reason about. That ease in reasoning typically leads to fewer accidental cyclic memory ownership patters (cyclic memory ownership == leaked memory). Coders who just slap down shared_ptr every time they see a pointer are far more likely to end up with memory ownership cycles.
That being said, cyclic memory ownership is also possible using only unique_ptr. And if it happens, you need weak_ptr to break the cycle, and weak_ptr only works with shared_ptr. So the introduction of an ownership cycle is another good reason to migrate to shared_ptr.