赞
踩
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。
以常用的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()
上面我们看到了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
的成员。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。