Qt的一些经验总结(3)-- d指针

254 阅读3分钟

背景

d指针的出现主要是为了解决C++编程中的二进制兼容性问题。在C++中,当一个类的私有成员发生变化时,会影响到类的大小和布局,从而导致二进制兼容性的破坏。为了解决这个问题,Qt采用了d指针这种设计模式(Pointer to IMPLementation,简称Pimpl),通过将私有数据成员放在一个单独的实现类中,并通过d指针来访问,从而保证了类的二进制兼容性不受影响​。

优点

  • 二进制兼容性:通过使用d指针,可以保持类的二进制兼容性。当类的私有成员发生变化时,不会影响到类的大小和布局,这样就能保证类的二进制兼容性不受影响。

  • 封装:d指针可以帮助更好地封装类的实现细节。通过将私有数据成员放在一个单独的实现类中,并通过d指针来访问,可以将类的实现细节从类的声明中分离出来,使得类的接口更加清晰​。

  • 减少编译依赖性:d指针可以减少头文件中的包含依赖,从而减少编译时的依赖性,这样当类的实现发生变化时,不需要重新编译依赖于该类的代码。

缺点

  • 增加代码复杂性:d指针的使用会增加代码的复杂性,因为需要在类的实现文件中定义一个额外的实现类,并且需要管理d指针的创建和销毁。

  • 可能影响性能:由于d指针需要额外的间接访问,可能会对性能产生一些负面影响,特别是在访问私有数据成员时。

使用

第一种

  • 创建私有类
class MyClassPrivate {
public:
    int someData;
    // ...其他私有数据成员
};
  • 在公有类中声明d指针
#include <QScopedPointer>

class MyClass {
    Q_DECLARE_PRIVATE(MyClass)
public:
    MyClass();
    ~MyClass();
    void someMethod();
    
private:
    QScopedPointer<MyClassPrivate> d_ptr;
};
  • 在公有类的构造函数和析构函数中管理d指针
MyClass::MyClass() : d_ptr(new MyClassPrivate) {
}

MyClass::~MyClass() {
    // QScopedPointer will automatically delete d_ptr
}
  • 在公有类的成员函数中使用d_func()访问私有数据
void MyClass::someMethod() {
    Q_D(MyClass);  // Q_D宏将d_func()的返回值赋给名为d的局部变量
    d->someData = 42;
    // ...使用d来访问其他私有数据成员
}

第二种(简化)

  • 在头文件中声明d指针
#include <QScopedPointer>

class MyClass {
public:
    MyClass();
    ~MyClass();
    void someMethod();
    
private:
    struct Private;
    QScopedPointer<Private> d;
};
  • 在cpp文件中实现Private
struct MyClass::Private
{
    int someData = 0;
};
  • 在公有类的构造函数和析构函数中管理d指针
MyClass::MyClass() : d(new Private) {
}

MyClass::~MyClass() {
}
  • 在公有类的成员函数中使用d_func()访问私有数据
void MyClass::someMethod() {
    d->someData = 42;
}

如何选择

一般情况下,使用简化的就可以,因为qt本身的d指针,包含了其他属性变量,这些属性变量,大多数都用在qt内部实现中,在应用层很少需要使用到。只有在需要使用到这些属性的情况下,才建议使用qt本身的d指针。