c – 在Qt中使用Pimpl成语,搜索简洁的方式
作者:互联网
我和Qt&的问题pimpl实际上不是问题,更多是对最佳实践建议的要求.
所以:我们有一个包含大量GUI和其他Qt类的大型项目.标头的可读性是精细协作所必需的,减少编译时间也是经常考虑的问题.
因此,我有很多类,如:
class SomeAwesomeClass: public QWidget
{
Q_OBJECT
public:
/**/
//interface goes here
void doSomething();
...
private:
struct SomeAwesomeClassImpl;
QScopedPointer<SomeAwesomeClassImpl> impl;
}
当然,Pimpl类在.cpp文件中,工作正常,如:
struct MonitorForm::MonitorFormImpl
{
//lots of stuff
}
这个软件应该是跨平台(不是一个惊喜)和交叉编译而不需要很大的努力.我知道Q_DECLARE_PRIVATE,Q_D和其他宏,它们让我更多地考虑Qt MOC,Qt版本可能存在差异(因为遗留代码),但这种方式还是有很多行代码类似于
impl->ui->component->doStuff();
//and
impl->mSomePrivateThing->doOtherStuff()
//and even
impl->ui->component->SetSomething(impl->mSomePrivateThing->getValue());
上面的伪代码是真实代码的简化版本,但我们大多数人都很好.但有些同事坚持认为,编写和阅读所有这些长线相当麻烦,特别是在impl-> ui> mSomething->经常重复.该意见指出,Qt marcos最终还为这种情况增添了视觉效果. Seversl #define可以提供帮助,但这些通常被认为是不好的做法.
总之,根据您的经验,有没有办法让pimpl使用更简洁?例如,在非库类中,它可能并不是真正需要的吗?根据具体情况,它的使用目标可能并不相同?
无论如何,烹饪它的正确方法是什么?
解决方法:
介绍
I know about Q_DECLARE_PRIVATE, Q_D and other macros
你了解它们,但是你真的使用它们并了解它们的目的,并且 – 在大多数情况下 – 它们的必然性吗?那些宏没有被添加以使东西变得冗长.他们在那里是因为你最终需要他们.
Qt版本之间的Qt PIMPL实现没有区别,但是当你从QClassPrivate继承时,你依赖于Qt的实现细节,你是否应该这样做. PIMPL宏与moc无关.您可以在不使用任何Qt类的普通C代码中使用它们.
唉,只要你以通常的方式实现PIMPL(也是Qt方式),就没有逃避你想要的东西了.
Pimpl-pointer vs this
首先,让我们观察impl代表这一点,但语言让你跳过使用这个 – >在多数情况下.因此,它没有什么太外国的.
class MyClassNoPimpl {
int foo;
public:
void setFoo(int s) { this->foo = s; }
};
class MyClass {
struct MyClassPrivate;
QScopedPointer<MyClassPrivate> const d;
public:
void setFoo(int s);
...
virtual ~MyClass();
};
void MyClass::setFoo(int s) { d->foo = s; }
继承要求……
但是,当你有继承时,事情变得普遍变得古怪:
class MyDerived : public MyClass {
class MyDerivedPrivate;
QScopedPointer<MyDerivedPrivate> const d;
public:
void SetBar(int s);
};
void MyDerived::setFooBar(int f, int b) {
MyClass::d->foo = f;
d->bar = b;
}
您将要在基类中重用单个d指针,但在所有派生类中它将具有错误的类型.因此,你可能会想到铸造它 – 这是更多的样板!相反,您使用一个返回正确转换的d指针的私有函数.现在,您需要派生公共类和私有类,并且您需要私有类的私有标头,以便派生类可以使用它们.哦,你需要将指针传递给派生的pimpl到基类 – 因为这是你可以初始化d_ptr同时保持它的唯一方式,因为它必须是.请参阅 – Qt的PIMPL实现是冗长的,因为您确实需要所有这些实现来编写安全,可组合,可维护的代码.没办法解决它.
MyClass1.h
class MyClass1 {
protected:
struct Private;
QScopedPointer<Private> const d_ptr;
MyClass1(Private &); // a signature that won't clash with anything else
private:
inline Private *d() { return (Private*)d_ptr; }
inline const Private *d() const { return (const Private*)d_ptr; }
public:
MyClass1();
virtual ~MyClass1();
void setFoo(int);
};
MyClass1_p.h
struct MyClass1::Private {
int foo;
};
MyClass1.cpp
#include "MyClass1.h"
#include "MyClass1_p.h"
MyClass1::MyClass1(Private &p) : d_ptr(&p) {}
MyClass1::MyClass1() : d_ptr(new Private) {}
MyClass1::~MyClass1() {} // compiler-generated
void MyClass1::setFoo(int f) {
d()->foo = f;
}
MyClass2.h
#include "MyClass1.h"
class MyClass2 : public MyClass1 {
protected:
struct Private;
private:
inline Private *d() { return (Private*)d_ptr; }
inline const Private *d() { return (const Private*)d_ptr; }
public:
MyClass2();
~MyClass2() override; // Override ensures that the base had a virtual destructor.
// The virtual keyword is not used per DRY: override implies it.
void setFooBar(int, int);
};
MyClass2_p.h
#include "MyClass1_p.h"
struct MyClass2::Private : MyClass1::Private {
int bar;
};
MyClass2.cpp
MyClass2::MyClass2() : MyClass1(*new Private) {}
MyClass2::~MyClass2() {}
void MyClass2::setFooBar(int f, int b) {
d()->foo = f;
d()->bar = b;
}
继承,Qt方式
Qt的PIMPL宏负责实现d()函数.好吧,他们实现了d_func()然后你使用Q_D宏来获得一个简单的d局部变量.重写以上内容:
MyClass1.h
class MyClass1Private;
class MyClass1 {
Q_DECLARE_PRIVATE(MyClass1)
protected:
QScopedPointer<Private> d_ptr;
MyClass1(MyClass1Private &);
public:
MyClass1();
virtual ~MyClass1();
void setFoo(int);
};
MyClass1_p.h
struct MyClass1Private {
int foo;
};
MyClass1.cpp
#include "MyClass1.h"
#include "MyClass1_p.h"
MyClass1::MyClass1(MyClass1Private &d) : d_ptr(*d) {}
MyClass1::MyClass1() : d_ptr(new MyClass1Private) {}
MyClass1::MyClass1() {}
void MyClass1::setFoo(int f) {
Q_D(MyClass1);
d->foo = f;
}
MyClass2.h
#include "MyClass1.h"
class MyClass2Private;
class MyClass2 : public MyClass1 {
Q_DECLARE_PRIVATE(MyClass2)
public:
MyClass2();
~MyClass2() override;
void setFooBar(int, int);
};
MyClass2_p.h
#include "MyClass1_p.h"
struct MyClass2Private : MyClass1Private {
int bar;
};
MyClass2.cpp
MyClass2() : MyClass1(*new MyClass2Private) {}
MyClass2::~MyClass2() {}
void MyClass2::setFooBar(int f, int b) {
Q_D(MyClass2);
d->foo = f;
d->bar = b;
}
工厂简化了pimpl
对于密封的类层次结构(即用户未派生的位置),可以通过使用工厂从任何私有详细信息中清除接口:
接口
class MyClass1 {
public:
static MyClass1 *make();
virtual ~MyClass1() {}
void setFoo(int);
};
class MyClass2 : public MyClass1 {
public:
static MyClass2 *make();
void setFooBar(int, int);
};
class MyClass3 : public MyClass2 {
public:
static MyClass3 *make();
void setFooBarBaz(int, int, int);
};
实现
template <class R, class C1, class C2, class ...Args, class ...Args2>
R impl(C1 *c, R (C2::*m)(Args...args), Args2 &&...args) {
return (*static_cast<C2*>(c).*m)(std::forward<Args2>(args)...);
}
struct MyClass1Impl {
int foo;
};
struct MyClass2Impl : MyClass1Impl {
int bar;
};
struct MyClass3Impl : MyClass2Impl {
int baz;
};
struct MyClass1X : MyClass1, MyClass1Impl {
void setFoo(int f) { foo = f; }
};
struct MyClass2X : MyClass2, MyClass2Impl {
void setFooBar(int f, int b) { foo = f; bar = b; }
};
struct MyClass3X : MyClass3, MyClass3Impl {
void setFooBarBaz(int f, int b, int z) { foo = f; bar = b; baz = z;}
};
MyClass1 *MyClass1::make() { return new MyClass1X; }
MyClass2 *MyClass2::make() { return new MyClass2X; }
MyClass3 *MyClass3::make() { return new MyClass3X; }
void MyClass1::setFoo(int f) { impl(this, &MyClass1X::setFoo, f); }
void MyClass2::setFooBar(int f, int b) { impl(this, &MyClass2X::setFooBar, f, b); }
void MyClass3::setFooBarBaz(int f, int b, int z) { impl(this, &MyClass3X::setFooBarBaz, f, b, z); }
这是非常基本的草图,应该进一步完善.
标签:pimpl-idiom,c,qt 来源: https://codeday.me/bug/20190828/1746924.html