我正在尝试创建一个仍然使用 Qt 的 implicit sharing 的多态类型的 QList。 .
我的具体用例是将 QList 中的项目传递给 QtConcurrent::mapped .这些项目都来自一个基类,该基类定义了一个 QtConcurrent::mapped 将调用的虚函数。大多数存储的数据将是特定于子类的。这些项目可以在线程开始后进行编辑,给我留下两个主要选项,锁定或复制数据。我不想锁定,因为这会消除使用额外线程的大部分目的。另外,制作我的数据的完整拷贝似乎也很不可取。相反,我想使用 Qt 的隐式共享来只复制我更改的数据项,但是我似乎无法制作仍然使用隐式共享的多态类型的 QList。
QList by default uses implicit sharing , 所以乍一看似乎我们已经完成了。
QList<Base> list;
Derived derived_obj;
list.append(derived_obj); // this fails
然而,将子类附加到父类的 QList 将不起作用,并且 standard answer是改为使用 QSharedPointers 的 QList 到基类,它将接受将指针附加到子类。
QList<QSharedPointer<Base> > pointer_list;
QSharedPointer<Derived> derived_pointer;
pointer_list.append(derived_pointer); // this works but there is no copy-on-write
如果我使用 QSharedPointer 的 QList,将被复制的是 QSharedPointer 而不是我的多态类,这意味着我失去了我想要的写时复制功能。
我也看过使用 QSharedDataPointers 的 QList .
QList<QSharedDataPointer<Base> > data_pointer_list;
QSharedDataPointer<Derived> derived_data_pointer;
list.append(derived_data_pointer); // this fails
然而,就像 QList 本身一样,QSharedDataPointers 似乎不接受多态类型。
最佳答案
这失败了:
QList<QSharedDataPointer<Base>> list;
QSharedDataPointer<Derived> derived(new Derived);
list.append(derived);
注意 下面的替代方法是合并 PolymorphicShared 和 PolymorphicSharedBase 以直接向 QSharedDataPointer<>,而不对 QSharedData 派生类型提出特殊要求(例如,它不需要显式支持 clone)。这需要更多的工作。以下只是一种工作方法。
QSharedDataPointer 确实是您寻求的答案,绝对可以容纳多态 QSharedData。您需要将类型分成基于 QSharedData 的层次结构,以及另一个包装 QSharedDataPointer 的并行层次结构。 QSharedDataPointer 通常不打算由类的最终用户直接使用。它是用于实现隐式共享类的实现细节。
为了提高效率,QSharedDataPointer 是一种可以在位级别移动的小型类型。当存储在各种容器中时,它非常有效 - 特别是在可以利用类型特征来了解此属性的 Qt 容器中。使用QSharedDataPointer 的类的大小通常会加倍,如果我们使它本身多态,因此不这样做会有所帮助。当然,指向的数据类型可以是多态的。
首先,让我们定义一个相当通用的基类 PIMPL,您将在其上构建层次结构。可以将 PIMPL 类转储到调试流中并进行克隆。
// https://github.com/KubaO/stackoverflown/tree/master/questions/implicit-list-44593216
#include <QtCore>
#include <type_traits>
class PolymorphicSharedData : public QSharedData {
public:
virtual PolymorphicSharedData * clone() const = 0;
virtual QDebug dump(QDebug) const = 0;
virtual ~PolymorphicSharedData() {}
};
xxxData 类型是 PIMPL,不适合最终用户使用。用户应该使用 xxx 类型本身。然后,此共享类型包装多态 PIMPL 并使用 QSharedDataPointer 来存储 PIMPL。它公开了 PIMPL 的方法。
类型本身不是多态的,以节省虚拟表指针的大小。 as() 函数作为 dynamic_cast() 将通过将多态性重定向到 PIMPL。
class PolymorphicShared {
protected:
QSharedDataPointer<PolymorphicSharedData> d_ptr;
PolymorphicShared(PolymorphicSharedData * d) : d_ptr(d) {}
public:
PolymorphicShared() = default;
PolymorphicShared(const PolymorphicShared & o) = default;
PolymorphicShared & operator=(const PolymorphicShared &) = default;
QDebug dump(QDebug dbg) const { return d_ptr->dump(dbg); }
template <class T> typename
std::enable_if<std::is_pointer<T>::value, typename
std::enable_if<!std::is_const<typename std::remove_pointer<T>::type>::value, T>::type>
::type as() {
if (dynamic_cast<typename std::remove_pointer<T>::type::PIMPL*>(d_ptr.data()))
return static_cast<T>(this);
return {};
}
template <class T> typename
std::enable_if<std::is_pointer<T>::value, typename
std::enable_if<std::is_const<typename std::remove_pointer<T>::type>::value, T>::type>
::type as() const {
if (dynamic_cast<const typename std::remove_pointer<T>::type::PIMPL*>(d_ptr.data()))
return static_cast<T>(this);
return {};
}
template <class T> typename
std::enable_if<std::is_reference<T>::value, typename
std::enable_if<!std::is_const<typename std::remove_reference<T>::type>::value, T>::type>
::type as() {
Q_UNUSED(dynamic_cast<typename std::remove_reference<T>::type::PIMPL&>(*d_ptr));
return static_cast<T>(*this);
}
template <class T> typename
std::enable_if<std::is_reference<T>::value, typename
std::enable_if<std::is_const<typename std::remove_reference<T>::type>::value, T>::type>
::type as() const {
Q_UNUSED(dynamic_cast<const typename std::remove_reference<T>::type::PIMPL&>(*d_ptr));
return static_cast<T>(*this);
}
int ref() const { return d_ptr ? d_ptr->ref.load() : 0; }
};
QDebug operator<<(QDebug dbg, const PolymorphicShared & val) {
return val.dump(dbg);
}
Q_DECLARE_TYPEINFO(PolymorphicShared, Q_MOVABLE_TYPE);
#define DECLARE_TYPEINFO(concreteType) Q_DECLARE_TYPEINFO(concreteType, Q_MOVABLE_TYPE)
template <> PolymorphicSharedData * QSharedDataPointer<PolymorphicSharedData>::clone() {
return d->clone();
}
帮助您轻松使用具有派生数据类型的抽象基类。它将 d-ptr 转换为适当的派生 PIMPL 类型,并将构造函数参数转发给 PIMPL 的构造函数。
template <class Data, class Base = PolymorphicShared> class PolymorphicSharedBase : public Base {
friend class PolymorphicShared;
protected:
using PIMPL = typename std::enable_if<std::is_base_of<PolymorphicSharedData, Data>::value, Data>::type;
PIMPL * d() { return static_cast<PIMPL*>(&*this->d_ptr); }
const PIMPL * d() const { return static_cast<const PIMPL*>(&*this->d_ptr); }
PolymorphicSharedBase(PolymorphicSharedData * d) : Base(d) {}
template <typename T> static typename std::enable_if<std::is_constructible<T>::value, T*>::type
construct() { return new T(); }
template <typename T> static typename std::enable_if<!std::is_constructible<T>::value, T*>::type
construct() { return nullptr; }
public:
using Base::Base;
template<typename ...Args,
typename = typename std::enable_if<std::is_constructible<Data, Args...>::value>::type
> PolymorphicSharedBase(Args&&... args) :
Base(static_cast<PolymorphicSharedData*>(new Data(std::forward<Args>(args)...))) {}
PolymorphicSharedBase() : Base(construct<Data>()) {}
};
现在拥有 PIMPL 类型及其运营商的并行层次结构是一件简单的事情。首先,我们的层次结构中的一个基本抽象类型添加了两个方法。请注意 PolymorphicSharedBase 如何添加正确类型的 d() 访问器。
class MyAbstractTypeData : public PolymorphicSharedData {
public:
virtual void gurgle() = 0;
virtual int gargle() const = 0;
};
class MyAbstractType : public PolymorphicSharedBase<MyAbstractTypeData> {
public:
using PolymorphicSharedBase::PolymorphicSharedBase;
void gurgle() { d()->gurgle(); }
int gargle() const { return d()->gargle(); }
};
DECLARE_TYPEINFO(MyAbstractType);
然后,一个不添加新方法的具体类型:
class FooTypeData : public MyAbstractTypeData {
protected:
int m_foo = 0;
public:
FooTypeData() = default;
FooTypeData(int data) : m_foo(data) {}
void gurgle() override { m_foo++; }
int gargle() const override { return m_foo; }
MyAbstractTypeData * clone() const override { return new FooTypeData(*this); }
QDebug dump(QDebug dbg) const override {
return dbg << "FooType-" << ref << ":" << m_foo;
}
};
using FooType = PolymorphicSharedBase<FooTypeData, MyAbstractType>;
DECLARE_TYPEINFO(FooType);
还有另一种添加方法的类型。
class BarTypeData : public FooTypeData {
protected:
int m_bar = 0;
public:
BarTypeData() = default;
BarTypeData(int data) : m_bar(data) {}
MyAbstractTypeData * clone() const override { return new BarTypeData(*this); }
QDebug dump(QDebug dbg) const override {
return dbg << "BarType-" << ref << ":" << m_foo << "," << m_bar;
}
virtual void murgle() { m_bar++; }
};
class BarType : public PolymorphicSharedBase<BarTypeData, FooType> {
public:
using PolymorphicSharedBase::PolymorphicSharedBase;
void murgle() { d()->murgle(); }
};
DECLARE_TYPEINFO(BarType);
我们要验证 as() 方法是否按需要抛出:
template <typename F> bool is_bad_cast(F && fun) {
try { fun(); } catch (std::bad_cast) { return true; }
return false;
}
隐式共享类型的使用与 Qt 自己的此类类型的使用没有什么不同。我们还可以使用 as 代替 dynamic_cast 进行转换。
int main() {
Q_ASSERT(sizeof(FooType) == sizeof(void*));
MyAbstractType a;
Q_ASSERT(!a.as<FooType*>());
FooType foo;
Q_ASSERT(foo.as<FooType*>());
a = foo;
Q_ASSERT(a.ref() == 2);
Q_ASSERT(a.as<const FooType*>());
Q_ASSERT(a.ref() == 2);
Q_ASSERT(a.as<FooType*>());
Q_ASSERT(a.ref() == 1);
MyAbstractType a2(foo);
Q_ASSERT(a2.ref() == 2);
QList<MyAbstractType> list1{FooType(3), BarType(8)};
auto list2 = list1;
qDebug() << "After copy: " << list1 << list2;
list2.detach();
qDebug() << "After detach: " << list1 << list2;
list1[0].gurgle();
qDebug() << "After list1[0] mod: " << list1 << list2;
Q_ASSERT(list2[1].as<BarType*>());
list2[1].as<BarType&>().murgle();
qDebug() << "After list2[1] mod: " << list1 << list2;
Q_ASSERT(!list2[0].as<BarType*>());
Q_ASSERT(is_bad_cast([&]{ list2[0].as<BarType&>(); }));
auto const list3 = list1;
Q_ASSERT(!list3[0].as<const BarType*>());
Q_ASSERT(is_bad_cast([&]{ list3[0].as<const BarType&>(); }));
}
输出:
After copy: (FooType-1:3, BarType-1:0,8) (FooType-1:3, BarType-1:0,8)
After detach: (FooType-2:3, BarType-2:0,8) (FooType-2:3, BarType-2:0,8)
After list1[0] mod: (FooType-1:4, BarType-2:0,8) (FooType-1:3, BarType-2:0,8)
After list2[1] mod: (FooType-1:4, BarType-1:0,8) (FooType-1:3, BarType-1:0,9)
列表复制很浅,项目本身没有被复制:引用计数都是 1。分离后,所有数据项都被复制,但因为它们是隐式共享的,所以它们只增加了它们的引用计数。最后,一个项目被修改后,它会自动分离,引用计数回落到 1。
关于c++ - 具有写时复制的多态类的 QList?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44593216/
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
大家好!我想知道Ruby中未使用语法ClassName.method_name调用的方法是如何工作的。我头脑中的一些是puts、print、gets、chomp。可以在不使用点运算符的情况下调用这些方法。为什么是这样?他们来自哪里?我怎样才能看到这些方法的完整列表? 最佳答案 Kernel中的所有方法都可用于Object类的所有对象或从Object派生的任何类。您可以使用Kernel.instance_methods列出它们。 关于没有类的Ruby方法?,我们在StackOverflow
我正在使用Rails3.1并在一个论坛上工作。我有一个名为Topic的模型,每个模型都有许多Post。当用户创建新主题时,他们也应该创建第一个Post。但是,我不确定如何以相同的形式执行此操作。这是我的代码:classTopic:destroyaccepts_nested_attributes_for:postsvalidates_presence_of:titleendclassPost...但这似乎不起作用。有什么想法吗?谢谢! 最佳答案 @Pablo的回答似乎有你需要的一切。但更具体地说...首先改变你View中的这一行对此#
我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
我正在写一篇关于在Ruby中几乎一切都是对象的博客文章,我试图通过以下示例来展示这一点:classCoolBeansattr_accessor:beansdefinitialize@bean=[]enddefcount_beans@beans.countendend所以从类中我们可以看出它有4个方法(当然,除非我错了):它可以在创建新实例时初始化一个默认的空bean数组它可以计算它有多少个bean它可以读取它有多少个bean(通过attr_accessor)它可以向空数组写入(或添加)更多bean(也通过attr_accessor)但是,当我询问类本身它有哪些实例方法时,我没有看到默认