我有几个具有QList<ClassName *>类型属性的类。我使用原始指针来表示所有权。访问器将返回QList<QPointer<ClassName>>,因为我们不想发送原始指针。问题是,目前每个访问器大致如下所示:
QList<QPointer<ClassName>> values;
ClassName* value;
foreach(value, m_values){
values.append(QPointer<ClassName>(value));
}
return values;在某些情况下,我们使用与QList不同的容器,有时容器中的值是const。现在,我想知道是否有一种方法不需要我复制/粘贴到每个访问器,因为它感觉要么应该有更好的方法,要么我们正在做错误的事情。
发布于 2016-04-01 14:38:26
我使用原始指针来表示所有权。
那就别这样。在C++11中,酌情使用std::unique_ptr或std::shared_ptr。Qt本身使用QObject指针,而不暗示所有权。例如,QObject::children()返回一个QObjectList = QList<QObject*>。我认为使用QPointer使事情变得非常复杂。若要最小化对其他代码的影响,只需返回QObjectList。
如果对象列表是常量(但不一定是对象本身),则只需创建一次可访问的容器:
class MyClass {
std::list<QObject> m_gadgets;
QList<QPointer<QObject>> m_userGadgets; // or std::list<...>
public:
typedef const std::list<QObject> Gadgets;
MyClass() {
m_gadgets.emplace_back(...);
...
m_userGadgets.reserve(m_gadgets.size());
for (auto & gadget : m_gadgets)
m_userGadgets.push_back(QPointer(&gadget));
}
Gadgets & gadgets() const { return m_userGadgets; }
};正如您所看到的,您不需要使用原始指针来存储QObject,您可以使用例如std::list和emplacement,或者std::array。这暴露了API的脆弱性:它对内部使用的容器非常敏感。
通常习惯于通过迭代器访问容器。您可以通过迭代器公开对象容器,用户应该编写只需要特定黑匣子迭代器类型的代码。
例如:
class MyClass {
std::list<QObject> m_gadgets;
public:
typedef std::list<QObject>::const_iterator GadgetsConstIterator;
GadgetsConstIterator gadgetsBegin() const { return m_gadgets.begin(); }
GadgetsConstIterator gadgetsEnd() const { return m_gadgets.end(); }
};
void gadgetsUser(MyClass::GadgetsConstIterator begin, MyClass::GadgetsConstIterator end);无论GadgetsConstIterator的具体类型是什么,只要它属于相同的迭代器类别,并且用户没有对迭代器做出其他不合理的假设,这都是可行的。
您还可以将对象容器公开为通用容器,其中应该指示用户使用std::begin(container)和std::end(container)访问容器。这样,您甚至可以使用原始C数组(颤栗):
class MyClass {
QObject m_gadgets[5];
// or
std::array<QObject, 5> m_gadgets;
// or
std::list<QObject> m_gadgets;
public:
// adjust as necessary
typedef QObject Gadgets[5];
typedef const QObject ConstGadgets[5];
ConstGadgets & gadgets() const { return reinterpret_cast<ConstGadgets&>(m_gadgets); }
Gadgets & gadgets() { return m_gadgets; }
}
void gadgetsUser1(MyClass::ConstGadgets & gadgets) {
for (auto gadget : gadgets)
qDebug() << gadget.metaObject()->className();
}
void gadgetsUser2(MyClass::ConstGadgets & gadgets) {
for (auto it = std::begin(gadgets); it != std::end(gadgets); it++)
qDebug() << gadget.metaObject()->className();
}最后,您还可以完全隐藏集合类型,并且只公开经过容器的烘焙forEach:
class MyClass {
std::list<QObject> m_gadgets;
public:
template <typename F> forEachGadget(F && fun) const {
for (auto const & gadget : m_gadget) fun(gadget);
}
template <typename F> forEachGadget(F && fun) {
for (auto & gadget : m_gadget) fun(gadget);
}
};
void OtherClass::gadgetUser(MyClass & c) {
c.forEachGadget([this](const QObject & gadget) { qDebug() << &gadget; }
}这里有很多可能的变化,根据最自然的感觉选择它们。但是,在所有情况下,用户代码都不能依赖于它接收到的访问对象的具体容器或迭代器类型。
最后,您不应该忘记QObject是一个QObject容器。您可以通过简单地将对象存储为容器对象的子对象来提供更大的灵活性。这样,您可以使用相同的容器类型返回对象列表和树:
class MyClass {
QObject m_gadgets;
public:
MyClass() {
new QObject(&m_gadgets); // list element 1
new QObject(&m_gadgets); // list element 2
}
const QObject & gadgets() const { return m_gadgets; }
}
void gadgetsUser(const QObject & gadgets) {
for (auto gadget : gadgets.children()) { qDebug() << gadget; }
// or, DFS of a tree
for (auto gadget : gadgets.children()) {
qDebug() << gadget;
gadgetsUser(*gadget);
}
// or, BFS of a tree
for (auto gadget : gadgets.children())
qDebug() << gadget;
for (auto gadget : gadgets.children())
gadgetsUser(*gadget);
}请注意,QObject将其子列表保持为具有稳定顺序的内部列表。QObject::children()只返回一个对内部持有的子列表的引用,因此它的O(1)成本非常小。将子对象添加到对象后,会将对象追加到其子对象的内部列表中--尽管没有文档化,但至少从Qt4.0开始就是如此。
https://stackoverflow.com/questions/36357677
复制相似问题