How can I make an object know what container it is in?
I need a way to put various items into sets. Each item can be in only 1 set at a time. I also need to be able to ask the item which set it is in. I have 3 questions:
Is this a valid implementation?
How can I use this if I split the classes into multiple header files, because each class requires the other?
Can I use a reference instead of the pointer to Manager in item class (because I don't like pointers)?
class Manager {
public:
add_item(Item& item) {
items.insert(&item);
item.manager = this;
}
remove_item(Item& item) {
items.erase(&item);
item.manager = nullptr;
}
private:
std::unordered_set<Item*> items;
}
class Item {
public:
Manager* get_manager() {return manager;}
private:
Manager* manager;
}
Is this a valid implementation? How can I use this if I split the classes into multiple header files, because each class requires the other?
You should use forward declarations.
In a good Object Oriented Design your object should not know how it is stored. It looks like your object should not be responsible for the activity which needs to localize the object.
std::unordered_list<Item*> items;
This is not proper. Do You want to hash addresses of the objects?
Can I use a reference instead of the pointer to Manager in item class (because I don't like pointers)?
When passing to the function You can always replace pointers with references. There is no problem with it.
How can I make an object know what container it is in?
You should not need to do so ever in a proper class design. There are things like Dependency Injection, but I think that's beyond your actual needs.
Is this a valid implementation?
No
How can I use this if I split the classes into multiple header files, because each class requires the other?
Use forward declarations (have a look here for more details).
Can I use a reference instead of the pointer to Manager in item class (because I don't like pointers)?
No, you cannot.
More in depth beyond your actually asked questions, it seems to be a design issue in the end, and you're asking for XY problems.
That you don't like (raw) pointers is a very good guts feeling, what might be wrong with your actual design. Well, unfortunately you can't manage references using standard containers like std::unordered_set
.
What you can do though, is using smart pointers as provided from the Dynamic memory management facilities.
Primary decision you have to take is, which of the various smart pointers like std::shared_pointer
, std::weak_ptr
or std::unique_ptr
is the right one to manage your necessary ownership requirements semantically correct.
Also Manager
might not be the best naming choice for what you want to do. Lookup the classic Design Patterns please, if there's something fitting better from these.
For example, it sounds you need something like an Observer, that tracks a number of Item
changes/events, and forwards these to other registered Item
instances.
Because Observer
doesn't need to have ownership for any of the registered Item
instances, a std::weak_ptr
seems to be the right choice to reference any of them.
I need a way to put various items into sets. Each item can be in only 1 set at a time. I also need to be able to ask the item which set it is in. I have 3 questions:
Is this a valid implementation?
The basic ideas are sound, though it doesn't enforce your data model or defend against problematic scenarios. For an example of what you could do:
void add_item(Item& item) {
if (item.manager == this) return; // already owner
if (item.manager) // already has another owner...
item.manager->remove(item);
items.insert(&item);
item.manager = this;
}
How can I use this if I split the classes into multiple header files, because each class requires the other?
Why would you want to? The classes seem too simple for there to be any need, and it just complicates things. (The proper way to do it is with forward declaration headers.)
Can I use a reference instead of the pointer to Manager in item class (because I don't like pointers)?
Not with your current design, because your code sets item.Manager
to nullptr
after removing an Item
from a Manager
, which is a reasonable thing to do.
More generally, you do need to ensure the actual Item
objects' lifetimes span the time you're storing pointers to them in the Manager
objects. That may or may not be a natural consequence of the way your code's written, or easily achieved by calling item.manager->remove(item);
before the Item
's destruction.
上一篇: 我解释DI和IoC的方式有什么问题?
下一篇: 我如何让对象知道它在哪个容器中?