目录
- 什么是不透明指针(Opaque Pointer)
- 不透明指针在Qt代码中的应用
- 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
// 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
继承自QWidget
,QWdiget
继承自QObject
和QPaintDevice
。
其中和不透明指针相关的主要是如下3个地方:
// qlabel.h
// ...
class QLabelPrivate;
class Q_WIDGETS_EXPORT QLabel : public QFrame
{
// ...
private:
// ...
Q_DECLARE_PRIVATE(QLabel)
// ....
};
// ...
QLabelPrivate
声明(只是声明,没有引用和实现)了不透明指针的类型QLabel
最终继承自QObject
,QObject
中有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
的成员。