当前位置:   article > 正文

【Qt】不透明指针(Opaque Pointer)在Qt源码中的应用

【Qt】不透明指针(Opaque Pointer)在Qt源码中的应用

什么是不透明指针(Opaque Pointer)

GeeksforGeeks中给的定义如下:

An opaque pointer is a pointer that points to a data structure whose contents are not exposed at the time of its definition.
不透明指针是一种指针,这种指针指向的内容是不公开的。

文字描述太抽象,我们通过代码展示什么是不透明指针,为什么要使用它。

假设我们实现一个Person类,保存信息并支持打印,实现很简单:

// person.h
#ifndef _PERSON_H_
#define _PERSON_H_

#include <string>

class Person {
private:
    std::string name;
public:
    Person();
    void printInfo();
};

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
// person.cpp
#include <iostream>
#include "foo.h"

Person::Person(): name("Sam") {}

void Person::printInfo() {
    std::cout << name << std::endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

以这种方式实现,非常简单直接,足够我们自己使用了。但如果要作为共享库,提供给其他人使用,就可能出现问题

首先,在.h中,可以看到name属性,其他人大概可以猜测printInfo()的实现。
其次,如果我们修改代码实现,比如Person的属性增加一个年龄age:

// person.h
#ifndef _PERSON_H_
#define _PERSON_H_

#include <string>

class Person {
private:
    std::string name;
    int age;
public:
    Person();
    void printInfo();
};

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
// person.cpp
#include <iostream>
#include "foo.h"

Person::Person(): name("Sam"), age(18) {}

void Person::printInfo() {
    std::cout << name << " " << age << std::endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

此时,依赖我们库的代码,必须重新编译,否则会Crash。

不透明指针就可以解决上面两个问题,将代码改为如下形式:

// person.h
#ifndef _PERSON_H_
#define _PERSON_H_

struct PersonPrivate;
class Person {
private:
    PersonPrivate *d_ptr;
public:
    Person();
    void print();
};

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
// person.cpp
#include <iostream>
#include <string>

#include "foo.h"

struct PersonPrivate {
    std::string name;
    PersonPrivate():name("Sam") {}
};

Person::Person(): d_ptr(new PersonPrivate) {}

void Person::print() {
    std::cout << d_ptr->name << std::endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

其中d_ptr就是不透明指针,不透明指针隐藏了更多的实现细节,另外修改增加age时,无需修改.h只需要修改cpp为如下代码:

// person.cpp
#include <iostream>
#include <string>

#include "foo.h"

struct PersonPrivate {
    std::string name;
    int age;
    PersonPrivate():name("Sam"), age(10) {}
};

Person::Person(): d_ptr(new PersonPrivate) {}

void Person::print() {
    std::cout << d_ptr->name << " " << d_ptr->age << std::endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

而且这种实现,依赖我们库的程序,不需要重新编译,也可以正常运行,这就是所谓binary compatibility

不透明指针在Qt代码中的应用

以常用的QLabel为例,其源代码如下:

// qlabel.h
#ifndef QLABEL_H
#define QLABEL_H

#include <QtWidgets/qtwidgetsglobal.h>
#include <QtWidgets/qframe.h>
#include <QtGui/qpicture.h>
#include <QtGui/qtextdocument.h>

QT_REQUIRE_CONFIG(label);

QT_BEGIN_NAMESPACE


class QLabelPrivate;

class Q_WIDGETS_EXPORT QLabel : public QFrame
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE setText)
    Q_PROPERTY(Qt::TextFormat textFormat READ textFormat WRITE setTextFormat)
    Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
    Q_PROPERTY(bool scaledContents READ hasScaledContents WRITE setScaledContents)
    Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment)
    Q_PROPERTY(bool wordWrap READ wordWrap WRITE setWordWrap)
    Q_PROPERTY(int margin READ margin WRITE setMargin)
    Q_PROPERTY(int indent READ indent WRITE setIndent)
    Q_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks)
    Q_PROPERTY(Qt::TextInteractionFlags textInteractionFlags READ textInteractionFlags
               WRITE setTextInteractionFlags)
    Q_PROPERTY(bool hasSelectedText READ hasSelectedText)
    Q_PROPERTY(QString selectedText READ selectedText)

public:
    explicit QLabel(QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags());
    explicit QLabel(const QString &text, QWidget *parent=nullptr, Qt::WindowFlags f=Qt::WindowFlags());
    ~QLabel();

    QString text() const;

#if QT_DEPRECATED_SINCE(6,6)
    QPixmap pixmap(Qt::ReturnByValueConstant) const { return pixmap(); }
#endif
    QPixmap pixmap() const;

#ifndef QT_NO_PICTURE
#if QT_DEPRECATED_SINCE(6,6)
    QPicture picture(Qt::ReturnByValueConstant) const { return picture(); }
#endif
    QPicture picture() const;
#endif
#if QT_CONFIG(movie)
    QMovie *movie() const;
#endif

    Qt::TextFormat textFormat() const;
    void setTextFormat(Qt::TextFormat);

    QTextDocument::ResourceProvider resourceProvider() const;
    void setResourceProvider(const QTextDocument::ResourceProvider &provider);

    Qt::Alignment alignment() const;
    void setAlignment(Qt::Alignment);

    void setWordWrap(bool on);
    bool wordWrap() const;

    int indent() const;
    void setIndent(int);

    int margin() const;
    void setMargin(int);

    bool hasScaledContents() const;
    void setScaledContents(bool);
    QSize sizeHint() const override;
    QSize minimumSizeHint() const override;
#ifndef QT_NO_SHORTCUT
    void setBuddy(QWidget *);
    QWidget *buddy() const;
#endif
    int heightForWidth(int) const override;

    bool openExternalLinks() const;
    void setOpenExternalLinks(bool open);

    void setTextInteractionFlags(Qt::TextInteractionFlags flags);
    Qt::TextInteractionFlags textInteractionFlags() const;

    void setSelection(int, int);
    bool hasSelectedText() const;
    QString selectedText() const;
    int selectionStart() const;

public Q_SLOTS:
    void setText(const QString &);
    void setPixmap(const QPixmap &);
#ifndef QT_NO_PICTURE
    void setPicture(const QPicture &);
#endif
#if QT_CONFIG(movie)
    void setMovie(QMovie *movie);
#endif
    void setNum(int);
    void setNum(double);
    void clear();

Q_SIGNALS:
    void linkActivated(const QString& link);
    void linkHovered(const QString& link);

protected:
    bool event(QEvent *e) override;
    void keyPressEvent(QKeyEvent *ev) override;
    void paintEvent(QPaintEvent *) override;
    void changeEvent(QEvent *) override;
    void mousePressEvent(QMouseEvent *ev) override;
    void mouseMoveEvent(QMouseEvent *ev) override;
    void mouseReleaseEvent(QMouseEvent *ev) override;
#ifndef QT_NO_CONTEXTMENU
    void contextMenuEvent(QContextMenuEvent *ev) override;
#endif // QT_NO_CONTEXTMENU
    void focusInEvent(QFocusEvent *ev) override;
    void focusOutEvent(QFocusEvent *ev) override;
    bool focusNextPrevChild(bool next) override;


private:
    Q_DISABLE_COPY(QLabel)
    Q_DECLARE_PRIVATE(QLabel)
#if QT_CONFIG(movie)
    Q_PRIVATE_SLOT(d_func(), void _q_movieUpdated(const QRect&))
    Q_PRIVATE_SLOT(d_func(), void _q_movieResized(const QSize&))
#endif
    Q_PRIVATE_SLOT(d_func(), void _q_linkHovered(const QString &))

#ifndef QT_NO_SHORTCUT
    Q_PRIVATE_SLOT(d_func(), void _q_buddyDeleted())
#endif
    friend class QTipLabel;
    friend class QMessageBoxPrivate;
    friend class QBalloonTip;
};

QT_END_NAMESPACE

#endif // QLABEL_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147

QFrame继承自QWidgetQWdiget继承自QObjectQPaintDevice

其中和不透明指针相关的主要是如下3个地方:

// qlabel.h
// ... 
class QLabelPrivate;
class Q_WIDGETS_EXPORT QLabel : public QFrame
{
    // ...
private:
    // ...
    Q_DECLARE_PRIVATE(QLabel)
    // ....
};
// ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • QLabelPrivate声明(只是声明,没有引用和实现)了不透明指针的类型
  • QLabel最终继承自QObjectQObject中有d_ptr属性
    在这里插入图片描述
  • Q_DECLARE_PRIVATE(QLabel)利用宏的方式给QLabel类添加友元QLabelPrivate,以及获取d_ptr的方法d_func()
    在这里插入图片描述
    至于QLabelPrivate的具体实现,我们作为外人就不得而知了。这种实现在Qt源码中随处可见

Qt中与不透明指针相关的一些宏

上面我们看到了Q_DECLARE_PRIVATE,就是将某个类对应的Private类添加为它的友元,并声明获取d_ptr的方法d_func()

另外还有Q_D

#define Q_D(Class) Class##Private * const d = d_func()
  • 1

其作用是在某个类中使用其Private类的成员,比如在QLabel实现中的某个函数中,可能就有Q_D(QLabel),那么该函数中可以直接使用d->的方式调用QLabelPrivate的成员。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/225136
推荐阅读
相关标签
  

闽ICP备14008679号