我需要一种方法把不同的东西放在一起。每个项目一次只能在一个集合中。我还需要能够问的项目设置它是在。我有3个问题:
这是有效的实现吗?
如果我将类拆分为多个头文件,因为每个类都需要另一个头文件,那么如何使用它?
我可以在项类中使用引用而不是指向管理器的指针吗(因为我不喜欢指针)?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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;
} |
- 至于你的头衔:你为什么需要?听起来你有一个非常严重的设计缺陷!
- 我有很多节点,它们是相连的。管理器是连接各个节点的"连接"。我需要能够用一个连接连接任意多个节点。我还需要能够从节点中找到管理器对象本身的名称和其他信息(特别是,我需要找到通过此连接连接的其他节点)。
- 您打算如何从集合中检索对象?一旦你这样做了,你就会知道它是在哪个位置,对吗?
- @Theosandstrom使用std::shared_ptrs和std::weak_ptrs而不是与原始指针作斗争怎么样?
- 不要让物品知道它在哪个容器里。这几乎是个普遍的坏主意。
- @小狗,我认为这个问题的措辞有点糟糕。更重要的是,Item知道它与Manager连接的是什么。
- 通过引用传递一个项目,然后将其地址存储在容器中是一个非常糟糕的主意。这意味着除非你在代码中的其他地方非常小心,否则你最终会得到很多悬空的指针,而阅读代码的人会想知道你到底在做什么。如果您想要存储一个对象的地址(我无论如何都不推荐),那么首先让函数接受一个指针。
- @M.M.为什么首先接受一个指针更好?如果您接受一个引用,那么Manager有一个不变量,它没有任何空项。如果您接受一个指针,那么有人可以添加一个空项。
- @Chrisdraw函数可以检查这一点。如果你接受推荐信,你可能会得到一个悬空的推荐信。
- @m.m函数可以检查是否为空,但是调用代码必须记住检查是否实际添加了项。如果您不能接受空值,那么就不需要这样做。你可以很容易地得到悬空指针,就像悬空引用一样。
- 是的,但正常的语义是,接受引用的函数不应该存储指向所传递对象的指针;而接受指针的函数则可能。这是自我记录的一部分
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?
基本的想法是合理的,尽管它不强制您的数据模型或防御有问题的场景。例如,您可以做什么:
1 2 3 4 5 6 7
| 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?
你为什么要这么做?这些类看起来太简单了,不需要任何需求,而且它只是把事情复杂化了。(正确的方法是使用前向声明头。)
Can I use a reference instead of the pointer to Manager in item class (because I don't like pointers)?
不适用于您当前的设计,因为您的代码在从Manager中删除Item后将item.Manager设置为nullptr,这是一个合理的操作。
更一般地说,您需要确保实际的Item对象的生命周期是您在Manager对象中存储指向它们的指针的时间。这可能是或不是您的代码编写方式的自然结果,也可能是在Item被破坏之前通过调用item.manager->remove(item);很容易实现的。
- 我想他们的原因分为单独的头文件,这些都不是我实际的类。我使用的是虚拟类的问题,我要证明我的solve,因为实际的类是更复杂的多。
- theosandstrom公平"的声音。好的,我说的是前向声明作为选项的方式做它,例如Manager.fwd.h只包含线是由Manager.h(包括class Manager;,确保他们保持一致和Item.h)。
How can I make an object know what container it is in?
你不应该在一个适当的类设计中这样做。有些事情像依赖注入,但我认为这超出了你的实际需要。
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)?
不,你不能。
更深入地说,除了你实际提出的问题之外,它最终似乎是一个设计问题,而你提出的是xy问题。
你不喜欢(原始)指针是一种很好的直觉,你的实际设计可能有什么问题。不幸的是,您不能使用像std::unordered_set这样的标准容器来管理引用。
但是,您可以使用动态内存管理工具提供的智能指针。
你必须做的主要决定是,在诸如std::shared_pointer、std::weak_ptr或std::unique_ptr等各种智能指针中,哪一个是正确地管理必要的所有权要求的权利。
另外,对于您想要做的事情,Manager可能不是最佳的命名选择。如果有更好的设计,请查找经典的设计模式。
例如,听起来您需要一个类似于观察者的东西,它跟踪许多Item更改/事件,并将这些更改/事件转发给其他注册的Item实例。
由于Observer不需要对任何已注册的Item实例拥有所有权,因此std::weak_ptr似乎是引用其中任何一个实例的正确选择。
- "我可以使用一个参考……"/不,你不能。"/"你不能使用"引用标准集装箱管理理论的std::unordered_set
没有问什么,他问了Manager& manager;在Item使用。
- "tonyd嗯,那都是漂亮的奇怪的线都在这里。主要是从行动上的污秽和要求来超过一个在一个时间的问题。我一直在为最通用的答案的问题,如是。
- 真的……rambling位的问题,但一个相当普通的要求和价值与试图帮助。frustrating组合!干杯。
- "tonyd嗯,我已经在我的最佳。这不只是帮助这里的代码,它是所有关于设计模式的概念和清规戒律。
- 如果我是使用一个弱_ PTR在观察,然后我有不会的商店共同_ PTR的item,它实际上是拥有吗?我可以使用一个弱_ PTR是什么,只是存储的值是什么?
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?
您应该使用正向声明。
在一个好的面向对象设计中,您的对象不应该知道它是如何存储的。看起来您的对象不应该负责需要本地化对象的活动。
1
| std::unordered_list<Item*> items; |
这是不恰当的。是否要散列对象的地址?
Can I use a reference instead of the pointer to Manager in item class
(because I don't like pointers)?
传递给函数时,始终可以用引用替换指针。这没问题。
- 你是说有一组指针是不正确的吗?或者我说的无序清单是错误的?
- @OOP中的theosandstrom应该坚持对象而不是它们的地址。您似乎希望将它们存储在std::set中的智能_指针中,提供均衡运算符,并以面向对象的方式对它们进行比较。
- 我需要把它们放在别处。将有另一个带有项向量的类。我需要跟踪这个新类中不同对象中项目之间的连接。
- 每个节点都应该有一个容器,其中包含到其他节点的弱指针。
- 但我也希望连接有自己的数据(例如名称)。按照您的方法,对于每个连接的项目,我需要将该名称存储数百次吗?这些连接需要跟踪成百上千个项目。
- @Theosandstrom弱指针不复制。您的结构可能如下所示:node std::vector>connections;data node data;
- 那么,我不需要将节点数据单独存储数百次吗?
- @每个节点一次。
- @Cyberguy"你总是可以用引用替换指针"对于任何标准容器都不是这样!
- @π?ε?ε?我的错,我以为他是在说把它传递给函数。固定答案。
- @Cyberguy我需要一些数据作为一个整体进行连接(例如名称),每个节点都需要访问这些数据。这些数据也需要存储在某个地方。它应该只进入每个节点都带有弱指针的对象中吗?这个类也可以携带弱指针到所有节点吗?
- 我怀疑没有人喜欢您的添加项实现的第一行。您正在传入对某个项的引用,然后针对该项执行的地址,并将该指针存储在容器中…这一实施打开了一个伤痛的世界。
- @我应该直接通过一个指针吗?
- @Theosandstrom"我需要一些数据来连接…",这取决于实际的语义和用例,如果您需要一个std::weak_ptr或std::shared_ptr。请阅读以下内容:动态内存管理。
- 那也会有同样的问题。你会将一个指针传递给一个生命周期不明确的项目。一般来说,您需要考虑将放入标准容器中的每个项目复制到其中。
- @为什么你假设物品的寿命不清楚?这可能是完全清楚的行动。
- @π?ε?ε?那么,我应该让实际存储节点的类保持指向它们的共享指针,并且让节点和管理器保持指向所有其他节点的弱指针吗?这似乎可行。
- @Theosandstrom从命名的角度来说,听起来更好,反之亦然,假设Manager类是用来创建对象的。可能是命名选择不当。你的经理班实际上应该管理什么?你的意思是说像一个Observer而不是?在后一种情况下,您应该使用std::weak_ptr。
- @π?ε?ε?我想这只是个糟糕的名字。我没有复制我的实际代码,只是一个显示我想做什么的虚拟版本。所以你认为这是一个很好的方法?
- @Theosandstrom看了我对你问题的第一条评论。我已经让你知道我是怎么想的了。
- 好啊。谢谢你的帮助。
- @Theosandstrom看看设计模式。这是几十年前激励我找到合适名字的来源之一。同时,您可以将这些仅作为标准术语;-)。
- @Theosandstrom还注意到,请每个问题问一个问题!其他一切都严重破坏了预期的FAQ/Q&A StackOverflow格式。
- 让我们在聊天中继续讨论。
假设Manager不被期望拥有Item,并且您对Item和Manager的生命周期有一个清晰的概念,那么这似乎是合理的。
如果要将类拆分为单独的头文件Item.h,只需要向前声明Manager。事实上,正如它所显示的那样,Item根本不需要Manager的完整定义,因此您可以简单地将其结构为:
项目h
1 2 3 4 5 6 7 8 9
| class Manager; // forward declaration
class Item {
public:
Manager* get_manager() const { return manager; }
void set_manager(Manager* new_manager) { manager = new_manager; }
private:
Manager* manager = nullptr; // Ensure initialized to null
}; |
经理H
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include"Item.h"
#include <unordered_set>
class Manager {
public:
void add_item(Item& item) {
if (item.get_manager())
return; // Item can have only one Manager
items.insert(&item);
item.set_manager(this);
}
void remove_item(Item& item) {
items.erase(&item);
item.set_manager(nullptr);
}
private:
std::unordered_set<Item*> items;
}; |
现场演示。
至于不喜欢指针,如果您想要一个可以为空的非拥有引用,那么指针是一个很好的匹配。指针通常比对成员变量的引用更合适,因为引用极大地限制了对类的操作。
- "……"如果你想要一个没有所有权的引用……我更喜欢std::weak_ptr,因为它在语义上是清晰的。
- "问吗?大吗?是吗?在我的观点是A A std::weak_ptr非拥有参考当你不有一个明确的想法lifetimes学院。如果你有一个明确的想法做的lifetimes然后一原-指针是精细。A是一个非std::weak_ptr拥有所有权的参考与选择在未来和SO可以造成混乱。
- "如果你有一个明确的想法做的lifetimes然后一原-指针是精细。"公平一点。anyway std::weak_ptr呆在安全的点位?)。