青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

子彈 の VISIONS

NEVER back down ~~

C++博客 首頁 新隨筆 聯系 聚合 管理
  112 Posts :: 34 Stories :: 99 Comments :: 0 Trackbacks
Designing Qt-Style C++ APIs
by Matthias Ettrich
We have done substantial research at Trolltech into improving the Qt development experience. In this article, I want to share some of our findings and present the principles we've been using when designing Qt 4, and show you how to apply them to your code.

 

Designing application programmer interfaces, APIs, is hard. It is an art as difficult as designing programming languages. There are many different principles to choose from, many of which tend to contradict each other.

Computer science education today puts a lot of emphasis on algorithms and data structures, with less focus on the principles behind designing programming languages and frameworks. This leaves application programmers unprepared for an increasingly important task: the creation of reusable components.

Before the rise of object-oriented languages, reusable generic code was mostly written by library vendors rather than by application developers. In the Qt world, this situation has changed significantly. Programming with Qt is writing new components all the time. A typical Qt application has at least some customized components that are reused throughout the application. Often the same components are deployed as part of other applications. KDE, the K Desktop Environment, goes even further and extends Qt with many add-on libraries that implement hundreds of additional classes.

But what constitutes a good, efficient C++ API? What is good or bad depends on many factors -- for example, the task at hand and the specific target group. A good API has a number of features, some of which are generally desirable, and some of which are more specific to certain problem domains.

Six Characteristics of Good APIs

An API is to the programmer what a GUI is to the end-user. The 'P' in API stands for "Programmer", not "Program", to highlight the fact that APIs are used by programmers, who are humans.

We believe APIs should be minimal and complete, have clear and simple semantics, be intuitive, be easy to memorize, and lead to readable code.

Finally, keep in mind that different kinds of users will use different parts of the API. While simply using an instance of a Qt class should be intuitive, it's reasonable to expect the user to read the documentation before attempting to subclass it.

 

The Convenience Trap

It is a common misconception that the less code you need to achieve something, the better the API. Keep in mind that code is written more than once but has to be understood over and over again. For example,

    QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical,
0, "volume");

is much harder to read (and even to write) than

    QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("volume");

 

The Boolean Parameter Trap

Boolean parameters often lead to unreadable code. In particular, it's almost invariably a mistake to add a bool parameter to an existing function. In Qt, the traditional example is repaint(), which takes an optional bool parameter specifying whether the background should be erased (the default) or not. This leads to code such as

    widget->repaint(false);

which beginners might read as meaning, "Don't repaint!"

The thinking is apparently that the bool parameter saves one function, thus helping reducing the bloat. In truth, it adds bloat; how many Qt users know by heart what each of the next three lines does?

    widget->repaint();
widget->repaint(true);
widget->repaint(false);

A somewhat better API might have been

    widget->repaint();
widget->repaintWithoutErasing();

In Qt 4, we solved the problem by simply removing the possibility of repainting without erasing the widget. Qt 4's native support for double buffering made this feature obsolete.

Here come a few more examples:

    widget->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Expanding, true);
textEdit->insert("Where's Waldo?", true, true, false);
QRegExp rx("moc_*.c??", false, true);

An obvious solution is to replace the bool parameters with enum types. This is what we've done in Qt 4 with case sensitivity in QString. Compare:

    str.replace("%USER%", user, false);               // Qt 3
str.replace("%USER%", user, Qt::CaseInsensitive); // Qt 4

 

Static Polymorphism

Similar classes should have a similar API. This can be done using inheritance where it makes sense -- that is, when run-time polymorphism is used. But polymorphism also happens at design time. For example, if you exchange a QListBox with a QComboBox, or a QSlider with a QSpinBox, you'll find that the similarity of APIs makes this replacement very easy. This is what we call "static polymorphism".

Static polymorphism also makes it easier to memorize APIs and programming patterns. As a consequence, a similar API for a set of related classes is sometimes better than perfect individual APIs for each class.

The Art of Naming

Naming is probably the single most important issue when designing an API. What should the classes be called? What should the member functions be called?

General Naming Rules

A few rules apply equally well to all kinds of names. First, as I mentioned earlier, do not abbreviate. Even obvious abbreviations such as "prev" for "previous" don't pay off in the long run, because the user must remember which words are abbreviated.

Things naturally get worse if the API itself is inconsistent; for example, Qt 3 has activatePreviousWindow() and fetchPrev(). Sticking to the "no abbreviation" rule makes it simpler to create consistent APIs.

Another important but more subtle rule when designing classes is that you should try to keep the namespace for subclasses clean. In Qt 3, this principle wasn't always followed. To illustrate this, we will take the example of a QToolButton. If you call name(), caption(), text(), or textLabel() on a QToolButton in Qt 3, what do you expect? Just try playing around with a QToolButton in Qt Designer:

  • The name property is inherited from QObject and refers to an internal object name that can be used for debugging and testing.
  • The caption property is inherited from QWidget and refers to the window title, which has virtually no meaning for QToolButtons, since they usually are created with a parent.
  • The text property is inherited from QButton and is normally used on the button, unless useTextLabel is true.
  • The textLabel property is declared in QToolButton and is shown on the button if useTextLabel is true.
In the interest of readability, name is called objectName in Qt 4, caption has become windowTitle, and there is no longer any textLabel property distinct from text in QToolButton.

 

Naming Classes

Identify groups of classes instead of finding the perfect name for each individual class. For example, All the Qt 4 model-aware item view classes are suffixed with View (QListView, QTableView, and QTreeView), and the corresponding item-based classes are suffixed with Widget instead (QListWidget, QTableWidget, and QTreeWidget).

Naming Enum Types and Values

When declaring enums, we must keep in mind that in C++ (unlike in Java or C#), the enum values are used without the type. The following example shows illustrates the dangers of giving too general names to the enum values:

    namespace Qt
{
enum Corner { TopLeft, BottomRight, ... };
enum CaseSensitivity { Insensitive, Sensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeft);
str.indexOf("$(QTDIR)", Qt::Insensitive);

In the last line, what does Insensitive mean? One guideline for naming enum types is to repeat at least one element of the enum type name in each of the enum values:

    namespace Qt
{
enum Corner { TopLeftCorner, BottomRightCorner, ... };
enum CaseSensitivity { CaseInsensitive,
CaseSensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeftCorner);
str.indexOf("$(QTDIR)", Qt::CaseInsensitive);

When enumerator values can be OR'd together and be used as flags, the traditional solution is to store the result of the OR in an int, which isn't type-safe. Qt 4 offers a template class QFlags<T>, where T is the enum type. For convenience, Qt provides typedefs for the flag type names, so you can type Qt::Alignment instead of QFlags<Qt::AlignmentFlag>.

By convention, we give the enum type a singular name (since it can only hold one flag at a time) and the "flags" type a plural name. For example:

    enum RectangleEdge { LeftEdge, RightEdge, ... };
typedef QFlags<RectangleEdge> RectangleEdges;

In some cases, the "flags" type has a singular name. In that case, the enum type is suffixed with Flag:

    enum AlignmentFlag { AlignLeft, AlignTop, ... };
typedef QFlags<AlignmentFlag> Alignment;

Naming Functions and Parameters

The number one rule of function naming is that it should be clear from the name whether the function has side-effects or not. In Qt 3, the const function QString::simplifyWhiteSpace() violated this rule, since it returned a QString instead of modifying the string on which it is called, as the name suggests. In Qt 4, the function has been renamed QString::simplified().

Parameter names are an important source of information to the programmer, even though they don't show up in the code that uses the API. Since modern IDEs show them while the programmer is writing code, it's worthwhile to give decent names to parameters in the header files and to use the same names in the documentation.

Naming Boolean Getters, Setters, and Properties

Finding good names for the getter and setter of a bool property is always a special pain. Should the getter be called checked() or isChecked()? scrollBarsEnabled() or areScrollBarEnabled()?

In Qt 4, we used the following guidelines for naming the getter function:

  • Adjectives are prefixed with is-. Examples:
    • isChecked()
    • isDown()
    • isEmpty()
    • isMovingEnabled()
    However, adjectives applying to a plural noun have no prefix:
    • scrollBarsEnabled(), not areScrollBarsEnabled()
  • Verbs have no prefix and don't use the third person (-s):
    • acceptDrops(), not acceptsDrops()
    • allColumnsShowFocus()
  • Nouns generally have no prefix:
    • autoCompletion(), not isAutoCompletion()
    • boundaryChecking()
    Sometimes, having no prefix is misleading, in which case we prefix with is-:
    • isOpenGLAvailable(), not openGL()
    • isDialog(), not dialog()
    (From a function called dialog(), we would normally expect that it returns a QDialog *.)
The name of the setter is derived from that of the getter by removing any is prefix and putting a set at the front of the name; for example, setDown() and setScrollBarsEnabled(). The name of the property is the same as the getter, but without the is prefix.

 

Pointers or References?

Which is best for out-parameters, pointers or references?

    void getHsv(int *h, int *s, int *v) const
void getHsv(int &h, int &s, int &v) const

Most C++ books recommend references whenever possible, according to the general perception that references are "safer and nicer" than pointers. In contrast, at Trolltech, we tend to prefer pointers because they make the user code more readable. Compare:

    color.getHsv(&h, &s, &v);
color.getHsv(h, s, v);

Only the first line makes it clear that there's a high probability that h, s, and v will be modified by the function call.

Case Study: QProgressBar

To show some of these concepts in practice, we'll study the QProgressBar API of Qt 3 and compare it to the Qt 4 API. In Qt 3:

    class QProgressBar : public QWidget
{
...
public:
int totalSteps() const;
int progress() const;
const QString &progressString() const;
bool percentageVisible() const;
void setPercentageVisible(bool);
void setCenterIndicator(bool on);
bool centerIndicator() const;
void setIndicatorFollowsStyle(bool);
bool indicatorFollowsStyle() const;
public slots:
void reset();
virtual void setTotalSteps(int totalSteps);
virtual void setProgress(int progress);
void setProgress(int progress, int totalSteps);
protected:
virtual bool setIndicator(QString &progressStr,
int progress,
int totalSteps);
...
};

The API is quite complex and inconsistent; for example, it's not clear from the naming that reset(), setTotalSteps(), and setProgress() are tightly related.

The key to improve the API is to notice that QProgressBar is similar to Qt 4's QAbstractSpinBox class and its subclasses, QSpinBox, QSlider and QDial. The solution? Replace progress and totalSteps with minimum, maximum and value. Add a valueChanged() signal. Add a setRange() convenience function.

The next observation is that progressString, percentage and indicator really refer to one thing: the text that is shown on the progress bar. Usually the text is a percentage, but it can be set to anything using the setIndicator() function. Here's the new API:

    virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;

By default, the text is a percentage indicator. This can be changed by reimplementing text().

The setCenterIndicator() and setIndicatorFollowsStyle() functions in the Qt 3 API are two functions that influence alignment. They can advantageously be replaced by one function, setAlignment():

    void setAlignment(Qt::Alignment alignment);

If the programmer doesn't call setAlignment(), the alignment is chosen based on the style. For Motif-based styles, the text is shown centered; for other styles, it is shown on the right hand side.

Here's the improved QProgressBar API:

    class QProgressBar : public QWidget
{
...
public:
void setMinimum(int minimum);
int minimum() const;
void setMaximum(int maximum);
int maximum() const;
void setRange(int minimum, int maximum);
int value() const;
virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;
Qt::Alignment alignment() const;
void setAlignment(Qt::Alignment alignment);
public slots:
void reset();
void setValue(int value);
signals:
void valueChanged(int value);
...
};

 

How to Get APIs Right

APIs need quality assurance. The first revision is never right; you must test it. Make use cases by looking at code which uses this API and verify that the code is readable.

Other tricks include having somebody else use the API with or without documentation and documenting the class (both the class overview and the individual functions).

Documenting is also a good way of finding good names when you get stuck: just try to document the item (class, function, enum value, etc.) and use your first sentence as inspiration. If you cannot find a precise name, this is often a sign that the item shouldn't exist. If everything else fails and you are convinced that the concept makes sense, invent a new name. This is, after all, how "widget", "event", "focus", and "buddy" came to be.

posted on 2008-05-30 16:51 子彈のVISIONS 閱讀(817) 評論(1)  編輯 收藏 引用 所屬分類: 2.0 工作參考

Feedback

# re: Designing Qt-Style C++ APIs 2008-05-30 16:52 子彈

設計Qt風格的C++API

作者Matthias Ettrich,譯者Googol Lee,原文地址在這里

在奇趣(Trolltech),為了改進Qt的開發體驗,我們做了大量的研究。這篇文章里,我打算分享一些我們的發現,以及一些我們在設計Qt4時用到的原則,并且展示如何把這些原則應用到你的代碼里。

設計應用程序接口,API,是很難的。這是一門和設計語言同樣難的藝術。這里可以選擇太多的原則,甚至有很多原則和其他原則有矛盾。

現在,計算機科學教育把很大的力氣放在算法和數據結構上,而很少關注設計語言和框架背后的原則。這讓應用程序員完全沒有準備去面對越來越重要的任務:創造可重用的組件。

在面向對象語言普及之前,可重用的通用代碼大部分是由庫提供者寫的,而不是應用程序員。在Qt的世界里,這種狀況有了明顯的改善。在任何時候,用Qt編程就是寫新的組件。一個典型的Qt應用程序至少都會有幾個在程序中反復使用的自定義組件。一般來說,同樣的組件會成為其他應用程序的一部分。KDE,K桌面環境,走得更遠,用許多追加的庫來擴展Qt,實現了數百個附加類。(一般來說,一個類就是一個可重用組件,原文這里沒有寫清楚。)

但是,一個好的,高效的C++ API是由什么組成的呢?是好還是壞,取決于很多因素——比如,手頭的工作和特定的目標群體。好的API有很多特性,一些特性是大家都想要的,而另一些則是針對特定問題域的。

好的API的六個特性

API是面向程序員的,用來描述提供給最終用戶的GUI是什么樣子。API中的P帶表程序員(Programmer),而不是程序(Program),用來強調API是給程序員用的,給人類的程序員用的。

我們堅信API應該是最小化且完整的,擁有清晰且簡單的語義,直覺化,容易記憶,并且引導人寫出易讀的代碼。

  • 最小化:最小化的API是指一個類盡可能只擁有最少的公開成員且盡可能只擁有最少的類。這個原則可以讓API更簡單易懂,更好記,更容易除錯,且更容易改變。
  • 完整的:完整的API是指要提供所有期望的功能。這個可能與最小化原則相沖突。另外,如果一個成員函數屬于一個不應該屬于的類,很多潛在的使用者都會找不到這個函數。
  • 擁有清晰且簡單的語義:就像其他設計工作一樣,你必須遵守最小驚奇原則(the principle of least surprise)。讓常見的任務簡單易行。不常見的工作可行,但不會讓用戶過分關注。解決特殊問題時,不要讓解決方案沒有必要的過度通用。(比如,Qt3中的QMimeSourceFactory可以通過調用QImageLoader來實現不同的API。)
  • 直覺化:就像電腦上的其他東西一樣,API必須是直覺化的。不同的經驗和背景會導致在判斷什么是直覺而什么不是時不同的感覺。如果一個中級用戶不讀文檔就可以使用(a semi-experienced user gets away without reading the documentation,沒懂這里的get away該怎么翻譯),并且一個程序員不懂API就可以理解縮寫的代碼,這種API就是直覺化的。
  • 易于記憶:讓API易于記憶,使用統一且精確的命名方法。使用可識別的模式和概念,并且避免縮寫。
  • 引導易讀的代碼(Lead to readable code):代碼一經寫就,會讀(并且除錯和修改)多次。易讀的代碼可能會花點時間來寫,但是可以節省產品周期中的其他時間。

最后,記住,不同類型的用戶會用到API的不同部分。雖然簡單的實例化一個Qt類是非常直覺化的,讓資深專家在試圖子類化之前讀一遍文檔,是很合理的。

便利陷阱

這是個常見的誤解:更好的API,用更少的代碼完成一件事。永遠記住代碼一次寫就,之后需要不斷的閱讀并理解。比如:

    QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical,
0, "volume");

遠比下面那樣難讀(甚至難寫):

    QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("volume");

布爾參數陷阱

布爾參數通常會導致不易讀的代碼。更進一步,給一個已經存在的函數加入一個布爾參數,這常常是個錯誤。在Qt里,一個傳統的例子是repaint(),這個函數帶有一個布爾參數,來標識是否擦除背景(默認擦除)。這讓代碼通常寫成:

    widget->repaint(false);

初學者很容易把這句話理解成“別重畫”!

這樣做是考慮到布爾參數可以減少一個函數,避免代碼膨脹。事實上,這反而增加了代碼量。有多少Qt用戶真的記住了下面三行程序都是做什么的?

    widget->repaint();
widget->repaint(true);
widget->repaint(false);

一個好一些的API可能看起來是這樣:

    widget->repaint();
widget->repaintWithoutErasing();

在Qt4里,我們重新設計了widget,使得用戶不再需要不重畫背景的重畫widget,來解決這個問題。Qt4原生支持雙緩存,廢掉了這個特性。

這里還有一些例子:

    widget->setSizePolicy(QSizePolicy::Fixed,
QSizePolicy::Expanding, true);
textEdit->insert("Where's Waldo?", true, true, false);
QRegExp rx("moc_*.c??", false, true);

一個顯而易見的解決方法是,使用枚舉類型代替布爾參數。這正是我們在Qt4中QString大小寫敏感時的處理方法。比較:

    str.replace("%USER%", user, false);               // Qt 3
str.replace("%USER%", user, Qt::CaseInsensitive); // Qt 4

靜態多態

相似的類應該含有相似的API。在必要的時候——就是說,需要使用運行時多態的時候——這可以通過繼承實現。但是多態依舊會發生在設計時期。比如,如果你用QListBox代替QComboBox,或者用QSlider代替QSpinBox,你會發現相似的API使這種替換非常容易。這就是我們所說的“靜態多態”。

靜態多態也使API和程序模式更容易記憶。作為結論,一組相關類使用相似的API,有時要比給每個類提供完美的單獨API,要好。

(譯注:C++ 0x將要引入的concept,就是靜態多態的語法層實現。這個要比單獨的函數名相似更強大且易用。)

命名的藝術

命名,大概是設計API時唯一最重要的問題了。該怎么稱呼這個類?成員函數該叫什么?

通用的命名規則

一些規則通常對所有名字都是有用的。首先,就像我之前提到的,別用縮寫。甚至很明顯的縮寫,比如“prev”表示“previous”從長遠看也是不劃算的,因為用戶必須記住哪些詞是縮寫。

如果API本身不一致,事情自然會變得很糟糕,比如, Qt3有activatePreviousWindow()和fetchPrev()。堅持“沒有縮寫”的規則更容易創建一致的API。

另一個重要但更加微妙的規則是,在設計類的時候,必須盡力保證子類命名空間的干凈。在Qt3里,沒有很好的遵守這個規則。比如,拿QToolButton來舉例。如果你在Qt3里,對一個QToolButton調用name()、caption()、text()或者textLabel(),你希望做什么呢?你可以在Qt Designer里拿QToolButton試試:

  • name屬性繼承自QObject,表示一個對象用于除錯和測試的內部名字。
  • caption屬性繼承自QWidget,表示窗口的標題,這個標題在視覺上對QToolButton沒有任何意義,因為他們總是跟隨父窗口而創建。
  • text屬性繼承自QButton,一般情況下是按鈕上現實的文字,除非useTextLabel為真。
  • textLabel在QToolButton里聲明,并且在useTextLabel為真時顯示在按鈕上。

由于對可讀性的關注,name在Qt4里被稱作objectName,caption變成了windowsTitle,而在QToolButton里不再有單獨的textLabel屬性。

給類命名

標識一組類而不是單獨給每個類找個恰當的名字。比如,Qt4里所有模式感知項目的視圖類(model-aware item view classes)都擁有-View的后綴(QListViewQTableViewQTreeView),并且對應基于項目的類都用后綴-Widget代替(QListWidgetQTableWidgetQTreeWidget)。

給枚舉類型及其值命名

當聲明枚舉時,時刻記住,在C++(不像Java和C#)中,使用枚舉值不需要類型信息。下面的例子演示了給枚舉值起個太過常用的名字所引起的危害:

    namespace Qt
{
enum Corner { TopLeft, BottomRight, ... };
enum CaseSensitivity { Insensitive, Sensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeft);
str.indexOf("$(QTDIR)", Qt::Insensitive);

在最后一行,Insensitive是什么意思?一個用于命名枚舉值的指導思想是,在每個枚舉值里,至少重復一個枚舉類型名中的元素:

    namespace Qt
{
enum Corner { TopLeftCorner, BottomRightCorner, ... };
enum CaseSensitivity { CaseInsensitive,
CaseSensitive };
...
};
tabWidget->setCornerWidget(widget, Qt::TopLeftCorner);
str.indexOf("$(QTDIR)", Qt::CaseInsensitive);

當枚舉值可以用“或”連接起來當作一個標志時,傳統的做法是將“或”的結果作為一個int保存,這不是類型安全的。Qt4提供了一個模板類 QFlags<T>來實現類型安全,其中T是個枚舉類型。為了方便使用,Qt為很多標志類名提供了typedef,所以你可以使用類型 Qt::Alignment代替QFlags<Qt::AlignmentFlag>。

為了方便,我們給枚舉類型單數的名字(這樣表示枚舉值一次只能有一個標志),而“標志”則使用復數名字。比如:

    enum RectangleEdge { LeftEdge, RightEdge, ... };
typedef QFlags<RectangleEdge> RectangleEdges;

有些情況下,“標志“類使用了單數的名字。這時,枚舉類使用-Flag做后綴:

    enum AlignmentFlag { AlignLeft, AlignTop, ... };
typedef QFlags<AlignmentFlag> Alignment;

(這里為啥不是把”標志“類用-Flag做后綴,而是把枚舉值做后綴呢?感覺有點混淆……)

給函數和參數命名

給函數命名的一個規則是,名字要明確體現出這個函數是否有副作用。在Qt3,常數函數QString::simplifyWhiteSpace()違反了這個原則,因為它返回類一個QString實例,而不是像名字所提示的那樣,更改了調用這個函數的實例本身。在Qt4,這個函數被重命名為QString::simplified()。

參數名是程序員的重要信息來源,雖然在使用API時,并不直接展示在代碼里。由于現代IDE在程序員寫代碼時可以自動顯示參數名(就是自動感知或者自動補全之類的功能),值得花時間給頭文件里聲明的參數一個合適的名字,并且在文檔中也使用相同的名字。

給布爾值設置函數(Setter)、提取函數(Getter)和屬性命名

給布爾屬性的設置函數和提取函數一個合適的名字,總是非常痛苦的。提取函數應該叫做checked()還是isChecked()?scrollBarsEnabled()還是areScrollBarEnabled()?

在Qt4里,我們使用下列規則命名提取函數:

  • 形容類的屬性使用is-前綴。比如:
    • isChecked()
    • isDown()
    • isEmpty()
    • isMovingEnable()
    另外,應用到復數名詞的形容類屬性沒有前綴:
    • scrollBarsEnabled(),而不是areScrollBarsEnabled()
  • 動詞類的屬性不使用前綴,且不使用第三人稱(-s):
    • acceptDrops(),而不是acceptsDrops()
    • allColumnsShowFocus()
  • 名詞類的屬性,通常沒有前綴:
    • autoCompletion(),而不是isAutoCompletion()
    • boundaryChecking()
    有時,沒有前綴就會引起誤解,這種情況使用前綴is-:
    • isOpenGLAvailable(),而不是openGL()
    • isDialog(),而不是dialog()
    (通過調用dialogue()方法,正常情況下會期望返回一個QDialog*的實例。)

設置函數名字繼承自提取函數名,只是移掉了所有前綴,并使用set-做前綴,比如:setDown()還有setScrollBarsEnabled()。屬性的名字與提取函數相同,只是去掉了前綴。

指針還是引用?

傳出參數的最佳選擇是什么,指針還是引用?

    void getHsv(int *h, int *s, int *v) const
void getHsv(int &h, int &s, int &v) const

大部分C++書推薦在能用引用的地方就用引用,這是因為一般認為引用比指針更“安全且好用”。然而,在奇趣(Trolltech),我們傾向使用指針,因為這讓代碼更易讀。比較:

    color.getHsv(&h, &s, &v);
color.getHsv(h, s, v);

只有第一行能清楚的說明,在函數調用后,h、s和v將有很大幾率被改動。

例子:QProgressBar

為了展示如何實際應用這些概念,我們將學習Qt3中的API QProgressBar并和Qt4里相通的API做比較。在Qt3里:

    class QProgressBar : public QWidget
{
...
public:
int totalSteps() const;
int progress() const;
const QString &progressString() const;
bool percentageVisible() const;
void setPercentageVisible(bool);
void setCenterIndicator(bool on);
bool centerIndicator() const;
void setIndicatorFollowsStyle(bool);
bool indicatorFollowsStyle() const;
public slots:
void reset();
virtual void setTotalSteps(int totalSteps);
virtual void setProgress(int progress);
void setProgress(int progress, int totalSteps);
protected:
virtual bool setIndicator(QString &progressStr,
int progress,
int totalSteps);
...
};

API相當復雜,且不統一。比如,僅從名字reset()并不能理解其作用,setTotalSteps()和setProgress()是緊耦合的。

改進API的關鍵,是注意到QProgressBar和Qt4的QAbstractSpinBox類及其子類QSpinBoxQSliderQDial很相似。解決方法?用minimum、maximum和value代替progress和totalSteps。加入alueChanged()信號。加入setRange()函數。

之后觀察progressString、percentage和indicator實際都指一個東西:在進度條上顯示的文字。一般來說文字是百分比信息,但是也可以使用setIndicator()設為任意字符。下面是新的API:

    virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;

默認的文字信息是百分比信息。文字信息可以藉由重新實現text()而改變。

在Qt3 API中,setCenterIndicator()和setIndicatorFollowStyle()是兩個影響對齊的函數。他們可以方便的由一個函數實現,setAlignment():

    void setAlignment(Qt::Alignment alignment);

如果程序員不調用setAlignment(),對齊方式基于當前的風格。對于基于Motif的風格,文字將居中顯示;對其他風格,文字將靠在右邊。

這是改進后的QProgressBar API:

    class QProgressBar : public QWidget
{
...
public:
void setMinimum(int minimum);
int minimum() const;
void setMaximum(int maximum);
int maximum() const;
void setRange(int minimum, int maximum);
int value() const;
virtual QString text() const;
void setTextVisible(bool visible);
bool isTextVisible() const;
Qt::Alignment alignment() const;
void setAlignment(Qt::Alignment alignment);
public slots:
void reset();
void setValue(int value);
signals:
void valueChanged(int value);
...
};

如何把API設計好(原文是How to Get APIs Right,我總想成We do APIs right……)

API需要質量保證。第一個修訂版不可能是正確的;你必須做測試。寫些用例:看看那些使用了這些API的代碼,并驗證代碼是否易讀。

其他的技巧包括讓別的人分別在有文檔和沒有文檔的情況下,使用這些API;或者為API類寫文檔(包括類的概述和獨立的函數)。

當你卡住時,寫文檔也是一種獲得好名字的方法:僅僅是嘗試把條目(類,函數,枚舉值,等等呢個)寫下來并且使用你寫的第一句話作為靈感。如果你不能找到一個精確的名字,這常常說明這個條目不應該存在。如果所有前面的事情都失敗了并且你確認這個概念的存在,發明一個新名字。畢竟,“widget”、 “event”、“focus”和“buddy”這些名字就是這么來的。

  回復  更多評論
  

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久中文字幕一区二区三区| 韩国在线一区| 日韩亚洲综合在线| 欧美激情一区三区| 欧美日韩不卡合集视频| 亚洲视频欧美视频| 亚洲一区二区三区在线播放| 国产美女精品视频| 欧美国产1区2区| 亚洲每日在线| 亚洲四色影视在线观看| 国产视频在线观看一区| 麻豆精品传媒视频| 欧美黑人一区二区三区| 亚洲一区二区欧美| 香港久久久电影| 亚洲国产精品久久久久久女王| 亚洲精品久久久蜜桃| 欧美风情在线观看| 欧美亚洲专区| 麻豆精品视频| 亚洲尤物在线视频观看| 久久爱www.| 这里是久久伊人| 久久成人精品电影| 99在线|亚洲一区二区| 亚洲欧美日韩国产综合在线 | 久久久久久久久综合| 亚洲精品视频免费观看| 亚洲免费视频一区二区| 亚洲国产成人精品女人久久久| 这里是久久伊人| 亚洲国产成人在线播放| 午夜精品福利一区二区蜜股av| 亚洲人成7777| 久久精品女人的天堂av| 亚洲一区二区三区成人在线视频精品| 久久九九99视频| 亚洲欧美综合另类中字| 欧美国产先锋| 麻豆freexxxx性91精品| 国产精品视频免费一区| 亚洲精品在线观看免费| 亚洲第一区在线| 午夜在线成人av| 亚洲欧美日韩高清| 欧美精品国产精品| 欧美肥婆bbw| 激情欧美日韩| 午夜在线一区| 欧美一级电影久久| 国产精品第三页| 亚洲精品中文字幕有码专区| 伊人久久亚洲热| 久久国产精品99久久久久久老狼| 亚洲无线一线二线三线区别av| 嫩草国产精品入口| 另类av一区二区| 一区二区在线视频播放| 久久久久久精| 免费成人高清在线视频| 激情久久久久久久久久久久久久久久| 亚洲一区视频| 久久狠狠一本精品综合网| 国产精品国产一区二区| 亚洲一级高清| 欧美一区二区三区免费在线看| 国产精品久久久久久久久免费樱桃 | 欧美高清在线播放| 亚洲激情网址| 欧美精品在线免费观看| 亚洲精品久久久久久下一站| 日韩亚洲一区在线播放| 欧美日韩在线一区二区| 亚洲作爱视频| 欧美在线日韩在线| 久久精品一二三区| 久久嫩草精品久久久精品| 红桃视频一区| 欧美α欧美αv大片| 亚洲国产裸拍裸体视频在线观看乱了 | 国产一区91精品张津瑜| 久久国产欧美精品| 欧美国产三区| 亚洲视屏一区| 国产日韩欧美制服另类| 久久久亚洲成人| 亚洲日本成人网| 国产精品99久久久久久www| 国产精品蜜臀在线观看| 久久精品中文字幕一区| 亚洲欧洲一区二区三区在线观看| 日韩一级免费| 国产欧美精品xxxx另类| 久热综合在线亚洲精品| 亚洲最新视频在线播放| 久久久爽爽爽美女图片| 日韩一区二区精品视频| 国产精品丝袜久久久久久app| 久久精品国产成人| 亚洲精品无人区| 欧美一级精品大片| 欧美va天堂在线| 亚洲女人小视频在线观看| 好看的日韩av电影| 欧美日韩大陆在线| 久久久久久一区| 宅男精品视频| 欧美激情二区三区| 久久久久久**毛片大全| 一区二区三区四区国产精品| 国内精品久久久久影院 日本资源| 欧美激情网友自拍| 久久精品国产清高在天天线| 亚洲毛片在线观看.| 欧美1区3d| 久久精品视频在线观看| 一区二区三区四区五区精品视频| 激情五月综合色婷婷一区二区| 欧美三级第一页| 欧美国产一区在线| 久久深夜福利免费观看| 午夜精品一区二区三区四区| 日韩小视频在线观看| 欧美不卡视频一区| 久久蜜桃精品| 久久久精品国产免费观看同学 | 国产午夜精品视频| 国产精品区二区三区日本| 欧美日韩国产美女| 欧美xxx成人| 米奇777在线欧美播放| 欧美中文字幕在线观看| 亚洲午夜免费视频| 在线视频一区二区| 夜久久久久久| 亚洲美女精品成人在线视频| 欧美国产日韩亚洲一区| 美女久久一区| 久久综合激情| 久久婷婷亚洲| 嫩草国产精品入口| 欧美va天堂va视频va在线| 麻豆国产va免费精品高清在线| 久久国产精品久久w女人spa| 欧美一区国产在线| 久久精品国产精品亚洲精品| 欧美在线看片| 久久久中精品2020中文| 久久一区二区精品| 你懂的亚洲视频| 亚洲国产成人在线| 亚洲毛片播放| 亚洲综合清纯丝袜自拍| 午夜一区不卡| 久久久久久日产精品| 噜噜爱69成人精品| 欧美激情一区二区在线| 欧美日韩国产综合视频在线| 国产精品a久久久久久| 国产精品美女在线观看| 国产一区深夜福利| 91久久国产综合久久蜜月精品| 亚洲精品国产精品国自产观看 | 亚洲二区精品| 在线一区二区三区四区| 香蕉久久精品日日躁夜夜躁| 久久国产精品亚洲77777| 美女脱光内衣内裤视频久久影院| 亚洲高清三级视频| 亚洲网站视频| 久久综合狠狠综合久久激情| 欧美伦理在线观看| 国产亚洲精品aa午夜观看| 亚洲国产精品成人综合| 亚洲综合精品自拍| 久久夜色撩人精品| 99视频一区二区三区| 久久精品国产2020观看福利| 欧美xx69| 国模私拍视频一区| 亚洲性图久久| 欧美成人性网| 性刺激综合网| 欧美日韩国产欧| 伊人久久噜噜噜躁狠狠躁| 在线中文字幕不卡| 蜜臀久久久99精品久久久久久| 亚洲精品一区在线观看| 久久九九国产精品| 国产精品久久久久aaaa| 亚洲精品久久嫩草网站秘色| 欧美一区二区三区在线免费观看| 亚洲国产经典视频| 欧美在线观看你懂的| 国产精品福利在线观看| 亚洲精品国久久99热| 久久综合色播五月| 午夜视频一区在线观看| 欧美丝袜第一区|