Development/Tutorials/Common Programming Mistakes

    From KDE TechBase
    Revision as of 14:28, 4 April 2007 by Aseigo (talk | contribs)
    (diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
    Common Programming Mistakes
    Tutorial Series   Getting Started
    Previous   None
    What's Next   n/a
    Further Reading   n/a

    Abstract

    This tutorial aims to combine the experience of KDE developers regarding Qt and KDE frameworks dos and don'ts. Besides actual mistakes, it also covers things which are not necessarily "bugs" but which make the code either slower or less readable.

    General C++

    This section guides you through some of the more dusty corners of C++ which either tend to be misused or which people often simply get wrong.

    Anonymous namespaces vs statics

    If you have a method in a class that does not access any members and therefore does not need an object to operate, make it static. If additionally it is a private helper function that is not needed outside of the file, make it a file-static function. That hides the symbol completely.

    Symbols defined in a C++ anonymous namespace do not have internal linkage. Anonymous namespaces only give an unique name for that translation unit and that is it; they don't change the linkage of the symbol at all. Linkage isn't changed on those because the second phase of two-phase name lookup ignores functions with internal linkages. Also, entities with internal linkage cannot be used as template arguments.

    So for now instead of using anonymous namespaces use static if you don't want a symbol to be exported.

    NULL pointer issues

    First and foremost: it is fine to delete a null pointer. So constructs like this that check for null before deleting are simply redundant:

    if ( ptr ) {

      delete ptr;
    

    }

    When you delete a pointer, make sure you also set it to 0 so that future attempts to delete that object will not fail in a double delete. So the complete and proper idiom is:

    delete ptr; ptr = 0;

    You many notice that null pointers are marked variously in one of three ways: 0, 0L and NULL. The argument against using NULL was that while C defines it as a 0 void pointer, C++ defines it to not be a 0 void pointer. All conforming C++ implementations will define NULL correctly so it's really not a problem. The argument for 0L was that it was handled correctly in variable argument functions, while 0 wasn't. Nowadays that's also an artifact.

    It's more a question of personal style and getting used to something. As far as the code in KDE's SVN goes you'll see 0 used more commonly than NULL.

    Member variables

    You'll encounter four major styles of marking class member variables in KDE:

    • m_variable lowercase m, underscore and the name of the variable starting with a lowercase letter. This is the most common style and one prefered for code in kdelibs.
    • mVariable lowercase m and the name of variable starting with a uppercase letter
    • variable_ variable name starting with a lowercase letter and then an underscore
    • _variable underscore and the name of variable starting with a lowercase letter. This style is actually usually frowned upon as this notation is also used in some code for function parameters instead.

    As it often happens there's no one correct way of doing it, so remember to always follow the syntax used by the application/library to which you are committing.

    Static variables

    Try to limit the number of static variables used in your code, especially when committing to a library. Construction and initialization of large number of static variables really hurts the startup times.

    Do not use class-static variables, especially not in libraries and loadable modules though it is even discouraged in applications. Static objects lead to lots of problems such as hard to debug crashes due to undefined order of construction/destruction.

    Instead, use a static pointer, together with K_GLOBAL_STATIC which is defined in kglobal.h and is used like this:

    class A { ... };

    K_GLOBAL_STATIC(A, globalA)

    void doSomething() {

        A *a = globalA;
        ...
    

    }

    void doSomethingElse() {

       if (globalA.isDestroyed()) {
           return;
       }
       A *a = globalA;
       ...
    

    }

    void installPostRoutine() {

       qAddPostRoutine(globalA.destroy);
    

    }

    See the API documentation for K_GLOBAL_STATIC for more information.

    Forward Declarations

    You will reduce compile times by forward declaring classes when possible instead of including their respective headers. For example:

    1. include <QWidget> // slow
    2. include <QStringList> // slow
    3. include <QString> // slow

    class SomeInterface { public:

       virtual void widgetAction( QWidget *widget ) =0;
       virtual void stringAction( const QString& str ) =0;
       virtual void stringListAction( const QStringList& strList ) =0;
    

    };

    The above should instead be written like this:

    class QWidget; // fast class QStringList; // fast class QString; // fast class SomeInterface { public:

       virtual void widgetAction( QWidget *widget ) =0;
       virtual void stringAction( const QString& str ) =0;
       virtual void stringListAction( const QStringList& strList ) =0;
    

    };

    Iterators

    Prefer to use const_iterators over normal iterators when possible. Containers, which are being implicitly shared often detach when a call to a non-const begin() or end() methods is made (List is an example of such a container). When using a const_iterator also watch out that you're really calling the const version of begin() and end(). Unless your container is actually const itself this probably won't be the case, possibly causing an unnecessary detach of your container. So basically whenever you use const_iterator initialize them using constBegin()/constEnd() instead, to be on the safe side.

    Cache the return of the end() method call before doing iteration over large containers. For example:

    QValueList<SomeClass> container;

    //code which inserts a large number of elements to the container

    QValueListConstIterator end( container.end() );

    for ( QValueListConstIterator itr( container.begin() );

        itr != end; ++itr ) {
    

    }

    This avoids the unnecessary creation of the temporary end() return object on each loop iteration, largely speeding it up.

    Prefer to use pre-increment over post-increment operators on iterators as this avoids creating an unnecessary temporary object in the process.