c++ - wort - subtiles verhalten




Böse Beispiele für subtil kaputten C++-Code (6)

Ich benötige einige Beispiele für schlechten C ++ - Code, die Verstöße gegen bewährte Methoden veranschaulichen. Ich wollte meine eigenen Beispiele finden, aber es fällt mir schwer, Beispiele zu finden, die nicht erfunden sind und bei denen eine Falle nicht sofort offensichtlich ist (es ist schwieriger, als es scheint).

Beispiele wären etwa:

  1. Definieren Sie std::auto_ptr Kopierkonstruktor für Klassen mit std::auto_ptr Mitgliedern und verwenden Sie std::auto_ptr Mitglieder mit vorwärts deklarierten Klassen.
  2. Aufrufen von virtuellen Funktionen von einem Konstruktor oder Destruktor (direkt oder indirekt).
  3. Überladen einer Vorlagenfunktion.
  4. Zirkelverweise mit boost::shared_ptr .
  5. Schneiden.
  6. Ausnahmen von C-Rückrufen auslösen (direkt oder indirekt).
  7. Gleitkommavergleich für Gleichheit.
  8. Ausnahmesicherheit von Konstruktoren mit rohen Zeigern.
  9. Werfen von Destruktoren.
  10. Integer-Überlauf beim Kompilieren auf verschiedenen Architekturen (Nichtübereinstimmung von size_t und int ).
  11. Ungültigmachen eines Container-Iterators.

... oder irgendein anderes böses Ding, an das du denken kannst.

Ich würde mich über einige Hinweise auf vorhandene Ressourcen oder ein oder zwei Beispiele freuen.



Argument-Dependent Lookup (ADL, auch Koenig-Lookup genannt) wird von den meisten C ++ - Programmierern nicht gut verstanden und kann zu sehr ungewöhnlichen Ergebnissen führen, insbesondere in Kombination mit Vorlagen.

In der Antwort auf Was sind die Tücken von ADL? Habe ich über eine große Tücke von ADL gesprochen.

Die Überlastungslösung ist sehr komplex. Bei der Verwendung von Direktiven im Namespace-Bereich treten häufig Probleme auf, insbesondere bei der using namespace std , da dieser Namespace eine große Anzahl von Entitäten mit gemeinsamen Namen enthält.

Hier sind zwei neuere Beispiele für die using namespace std die Probleme verursachen:


Dieser, IMHO, ist auch schwierig:

class Base {
int _value;

public:
    Base() {
        _value = g();
    }

    virtual int f() = 0;

    int g() { return f(); }
};

class Derived: Base {   
public:
    Derived(): Base()
    { /* init Derived */ }

    int f() { /* implementation */ }
}

Ihr Code stürzt ab, weil die rein virtuelle Methode f() nicht implementiert ist. Der offensichtliche Grund dafür ist, dass Derived im Konstruktor noch nicht vollständig ist, sodass Sie das virtuelle pure f() aufrufen und vom Compiler nicht erkannt werden (normalerweise beschwert sich der Compiler, wenn ein reines virtuelles Objekt in einem Konstruktor aufgerufen wird). .

Auf jeden Fall kann es vorkommen, dass ein virtuelles Pure aufgerufen wird, wenn Sie einen komplexen Konstruktor haben, der andere Member-Funktionen aufruft, und keine Unit-Tests vorhanden sind.


Was glaubst du, wird das Programm drucken?

#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;
}

Antwort: b , b , a ! Der erste Ausdruck ist offensichtlich. Der zweite ist b weil die Definition von B::f(bool) die Definition von A::f(int) verbirgt. Die dritte ist a weil eine Überlastungsauflösung beim statischen Typ auftritt.

(Quelle: Guru der Woche, aber ich kann den Artikel nicht finden.)


Die ärgerlichste Analyse ergibt sich aus der Art und Weise, wie C ++ Dinge wie diese analysiert:

// 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

Dies wird jeden überraschen, der mit dieser besonderen Eigenart der Sprache nicht vertraut ist (mich eingeschlossen, als ich anfing, C ++ zu lernen).


#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();
}

Und die Ausgabe ist:

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

Nicht sicher, ob es einen Namen hat, aber es ist sicher böse! : P





c++