0
点赞
收藏
分享

微信扫一扫

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

juneyale 03-12 15:30 阅读 3

目录

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

GeeksforGeeks中给的定义如下:

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

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

// person.h
#ifndef _PERSON_H_
#define _PERSON_H_

#include <string>

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

#endif
// person.cpp
#include <iostream>
#include "foo.h"

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

void Person::printInfo() {
    std::cout << name << std::endl;
}

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

首先,在.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
// person.cpp
#include <iostream>
#include "foo.h"

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

void Person::printInfo() {
    std::cout << name << " " << age << std::endl;
}

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

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

// person.h
#ifndef _PERSON_H_
#define _PERSON_H_

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

#endif
// 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;
}

其中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;
}

而且这种实现,依赖我们库的程序,不需要重新编译,也可以正常运行,这就是所谓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

QFrame继承自QWidgetQWdiget继承自QObjectQPaintDevice

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

// qlabel.h
// ... 
class QLabelPrivate;
class Q_WIDGETS_EXPORT QLabel : public QFrame
{
    // ...
private:
    // ...
    Q_DECLARE_PRIVATE(QLabel)
    // ....
};
// ...
  • 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()

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

举报

相关推荐

0 条评论