简体中文 English

Written By : Girish Ramakrishnan, ForwardBias Technologies

概述

Qt中的数据类型大致可分为两大类 – 值类型和指针类型。

值类型

In Qt, value based are those that you can treat akin to the C++ integral types like int, short, long. The copying and modification semantics for value based types has no ambiguity. For example,

  1. int x = 3;
  2. int y = x;
  3. x  = 10;

In the above code, there is no ambiguity on what the value of y is. There is also no ambiguity that the value of y is unaffected after a change to x i.e y still has value 3 after x has been set to 10.

Value based types have the following characteristics:

  • They have a copy constructor that creates a copy of the other variable
  • An assignment operator that assigns itself a copy of other variable
  • Changes to one variable never affects the value of another variable
  • Have a ‘==’ operator that helps you compare the values
  • They are usually allocated on the stack

In Qt, QString, QList, QVector are all value based types. You can assign one to the other and expect the value to be copied. For example,

  1. QString s1 = "tahera";
  2. QString s2 = s1;
  3. s1 = "athena";

In the above code, the assignment on line 2 creates a copy of content of s1 into s2. s1 and s2 can change independently. In the third line when we change the value of s1, s2 is unchanged.

It appears expensive to copy the contents of s1 into s2, especially so for types like lists and vectors. Qt uses a technique called implicit sharing [doc.trolltech.com] which makes such operations very efficient.

指针类型

Pointer based types are akin to objects of the real world. These types cannot be copied because it’s hard/impossible to define intuitive semantics for copying these types. For example,

  1. class Human {
  2.     ...
  3.    QString name;
  4.    QString fatherName;
  5. };
  6. Human h1;
  7. Human h2 = h1;

How is Human supposed to be copied? Is the human’s name copied too? How about his body features? You end up in a situation where we have two humans objects that are one and the same, which as we know never happens in the real world. In these cases, it probably makes more sense to have an explicit function human2.copyBodyFeatures(human1) and disable the = operator.

A more Qt’ish example is a Button class. What happens when you do ‘button2 = button1;’? Do you copy all attributes of button2 to button1? Widgets are usually part of a widget hierarchy. Does button2 have the same parent as button1 and has the same position/geometry as button1?

One tends to think of types like Button, Human as ‘objects’ or instances of a certain type. Such types are called pointer based types. They have the following characteristics:

  • They don’t have a copy constructor.
  • They don’t have an assignment operator.
  • They don’t have a operator. It doesn't make sense to compare two objects (using operator ), you can only compare properties of objects.
  • They derive from QObject
  • They are usually allocated on the heap. This is not necessary, however, if you want to take advantage of QObject’s memory management you have to allocate such types on the heap (see below).

Since such types cannot be copied or assigned, you would usually pass a pointer to these objects around.

QObject的内存管理

A feature of Qt is have a parent-child relationship between QObjects. Every QObject object has a parent QObject. When the parent QObject gets deleted or destroyed, it deletes it’s children. This feature of Qt makes memory management of QObject’s very simple. Just ensure that you delete all the top level QObjects and your code will not have any memory leaks. Deleting a top level window object will automatically delete all the child widgets. The deletion is performed by using the ‘delete’ operator. If any child QObject was created on the stack, your code will crash.

显式共享的数据类型

Since QObject has a built-in concept of memory management, it is usually obvious as to who owns the object when a function returns a pointer. For example, it is a fair guess that ‘QStatusBar *QMainWindow::statusBar() const’ returns a QStatusBar that is owned by the QMainWindow. The user is not expected to delete the QStatusBar.

However, ownership information is not so clear if the returned types is not a QObject. As an example consider,

  1. QWebElement *element = webPage->getElementByName("company_info");

Is the QWebElement pointer returned owned by the webPage or by the user? What we would like is a data type that looks like a value based type from a memory management perspective i.e users don’t think of deleting them) but behaves like a pointer based type i.e users continue to think of them as real world objects . Such ‘pseudo’ value based types are called explicitly shared data types. For example, the above API could have been designed as,

  1. QWebElement e1 = webPage->getElementByName("company_info"); // e1 appears as a value based type
  2. QWebElement e2 = e1; // can be copied. e1 and e2 are one and the same.
  3. e2.setAttribute(attrib, value); // any change in e2 reflects in e1 (unlike implicitly shared data types)

The user does not think about deleting e1 or e2. At the same time, it is clear that just because the stack variables e1 and e2 die, the actual elements don’t go away from the web page. The author likes to think of such types as glorified pointers – QWebElement is actually an interface to an internal structure of web page.

类型的传递语义

In C++, types can be passed around using 3 semantics – by value (copy), by pointer and by reference. Qt’s approach to passing around variables is thus:

  • Value based types are either passed by value or as a const-ref. For integral types like int, double passing by value (copy) is used. For Qt’s value based types like QString, QList, passing a const-ref provides a minor optimization (we don’t need to create a copy, even though the copying is extremely efficient because of implicit sharing).
  • Object based types are always passed by pointer. For example, QWidget, QObject are always passed as pointers.
  • For, readability, values are passed by pointer (as opposed to by reference) when the value itself is changed by a function. For example, writeToString(&aString) is preferred over writeToString(aString /* passed by ref */);. The former makes it obvious (since it’s the convention), that anything passed as pointer is expected to be modified.
  • Don’t return a const-ref for value based types
    1.     class Widget {
    2.      ...
    3.     const QColor &color() const { return m_color; } // BAD STYLE!
    4.      ...
    5.     private: QColor m_color;
    6.     };

    Reason: While it is true that there is a small performance boost to be gained by returning a const-ref, there is a problem with the above approach. Consider,
    1.     const QColor &color = widget->color();
    2.     delete widget;
    3.     // color is now dangling!

Note that if color() had returned by value, it is correct C++ to hold a const-ref to a temporary variable (i.e the copy returned by the function).

Categories: