39 votes

Échantillons maléfiques de code C++ subtilement cassé

J'ai besoin de quelques exemples de mauvais code C++ qui illustreront la violation des bonnes pratiques. Je voulais trouver mes propres exemples, mais j'ai du mal à trouver des exemples qui sont ne sont pas inventés, et où le piège n'est pas immédiatement évident (c'est plus difficile qu'il n'y paraît).

Voici quelques exemples :

  1. Ne pas définir de constructeur de copie pour les classes avec std::auto_ptr et en utilisant les std::auto_ptr avec des classes déclarées à l'avance.
  2. Appel de fonctions virtuelles à partir d'un constructeur ou d'un destructeur (directement ou indirectement).
  3. Surcharge d'une fonction modèle.
  4. Références circulaires avec boost::shared_ptr .
  5. Trancher.
  6. Lancer des exceptions à partir de rappels C (directement ou indirectement).
  7. Comparaison en virgule flottante pour l'égalité.
  8. Sécurité des exceptions pour les constructeurs dont les membres sont des pointeurs bruts.
  9. Lancer des destructeurs.
  10. Débordement d'un nombre entier lors de la compilation sur des architectures différentes (non-concordance de size_t y int ).
  11. Invalidation d'un itérateur de conteneur.

...ou toute autre chose maléfique à laquelle vous pouvez penser.

J'apprécierais que l'on m'indique des ressources existantes ou que l'on me donne un ou deux exemples.

35voto

In silico Points 30778

L'analyse la plus contrariante est un résultat étonnamment contre-intuitif de la façon dont le C++ analyse ce genre de choses :

// Declares a function called "myVector" that returns a std::vector<float>.
std::vector<float> myVector(); 
// Does NOT declare an instance of std::vector<float> called "myVector"

// Declares a function called "foo" that returns a Foo and accepts an unnamed
// parameter of type Bar.
Foo foo(Bar()); 
// Does NOT create an instance of Foo called "foo" nor creates a Bar temporary

// Declares a function called "myVector" that takes two parameters, the first named
// "str" and the second unnamed, both of type std::istream_iterator<int>.
std::vector<float> myVector( 
    std::istream_iterator<int>(str),
    std::istream_iterator<int>()
);
// Does NOT create an instance of `std::vector<float>` named "myVector" while copying
// in elements from a range of iterators

Cela surprendra à peu près tous ceux qui ne connaissent pas cette particularité du langage (moi y compris lorsque j'ai commencé à apprendre le C++).

16voto

Palmik Points 1664
#include <iostream>

class Base
{
    public:
        virtual void foo() const { std::cout << "A's foo!" << std::endl; }
};

class Derived : public Base
{
    public:
        void foo() { std::cout << "B's foo!" << std::endl; }
};

int main()
{
    Base* o1 = new Base();
    Base* o2 = new Derived();
    Derived* o3 = new Derived();

    o1->foo();
    o2->foo();
    o3->foo();
}

Et le résultat est le suivant :

A's foo!
A's foo!
B's foo!

Je ne sais pas si elle a un nom, mais elle est diabolique ! :P

13voto

In silico Points 30778

Les codes qui ne sont pas à l'abri des exceptions peuvent échouer d'une manière qui n'est pas évidente pour les lecteurs du code :

// Order of invocation is undefined in this context according to the C++ standard.
// It's possible to leak a Foo or a Bar depending on the order of evaluation if one
// of the new statements throws an exception before their auto_ptrs can "own" it
accept_two_ptrs(std::auto_ptr<Foo>(new Foo), std::auto_ptr<Bar>(new Bar));

void MyClass::InvokeCallback(CallbackType cb)
{
    Foo* resource = new Foo;
    cb(resource); // If cb throws an exception, resource leaks
    delete resource;
}

7voto

Dan Points 6319

Surcharge de l'opérateur d'affectation mais ne pas gérer correctement l'auto-assignation .

7voto

Neil G Points 7028

Que pensez-vous que le programme va imprimer ?

#include <iostream>
using namespace std;

struct A {
    void f(int) { cout << "a" << endl; }
};

struct B: public A {
    void f(bool) { cout << "b" << endl; }
};

int main() {
    B b;
    b.f(true);
    b.f(1);
    A* a = &b;
    a->f(true);
    return 0;
}

Réponse : b , b , a ! La première impression est évidente. La seconde est b car la définition de B::f(bool) cache la définition de A::f(int) . Le troisième est a car la résolution des surcharges se fait sur le type statique.

(source : Guru of the Week, mais je ne trouve pas l'article).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X