Frage

Ich möchte wissen, ob es möglich ist, Compiler Problem für Code eine Warnung / Fehler zu lassen, wie folgend:

Hinweis:

1. Ja, es ist eine schlechte Programmierstil und wir sollten solche Fälle vermeiden - aber wir haben es mit Legacy-Code und Hoffnung Compiler kann solche Fälle für uns identifizieren)

.

2. Ich ziehe eine Compiler-Option (VC ++) zu deaktivieren oder Objekt Slicing zu aktivieren, wenn es irgendwelche gibt.

class Base{};
class Derived: public Base{};

void Func(Base)
{

}

//void Func(Derived)
//{
//
//}

//main
Func(Derived());

Hier, wenn ich die zweite Funktion auf Kommentar, würde die erste Funktion genannt -. Und die Compiler (beide VC ++ und gcc) fühlt sich wohl mit dem

Ist es Standard C ++? und kann ich fragen Compiler (VC ++), um mir eine Warnung, wenn ein solcher Code erfüllt?

Vielen Dank !!!

Edit:

Danke alle so viel für Ihre Hilfe!

Ich kann keine Compiler-Option finden Sie einen Fehler / Warnung zu geben - in MSDN Forum für VC ++ Compiler Berater ohne Antwort, die ich dies auch geschrieben. So bin ich weder gcc noch vc Angst ++ diese Funktion implementiert.

So fügen Sie Konstruktor die abgeleiteten Klassen nehmen als Paramter die beste Lösung für jetzt sein würde.

Bearbeiten

Ich lege eine feedbak zu MS und hoffen, dass sie es beheben bald:

https://connect.microsoft.com/VisualStudio/feedback/ ViewFeedback.aspx? FeedbackID = 421.579

-Baiyan

War es hilfreich?

Lösung

Wenn Sie die Basisklasse ändern können Sie etwas tun könnten, wie:

class Base
{
public:
// not implemented will cause a link error
    Base(const Derived &d);
    const Base &operator=(const Derived &rhs);
};

auf Ihrem Compiler Je dass sollten Sie die Übersetzungseinheit erhalten, und vielleicht die Funktion, wo das Aufschneiden geschieht.

Andere Tipps

Als eine Variation von Andrew Khosravian Antwort , ich werde vorschlagen Templat Kopierkonstruktoren und Zuweisungsoperatoren verwenden. Auf diese Weise brauchen Sie nicht alle abgeleiteten Klassen einer bestimmten Basisklasse, um zu wissen, dass die Basisklasse gegen Aufschneiden zu schützen:

class Base
{
private:   // To force a compile error for non-friends (thanks bk1e)
// Not implemented, so will cause a link error for friends
    template<typename T> Base(T const& d);
    template<typename T> Base const& operator=(T const& rhs);

public:
// You now need to provide a copy ctor and assignment operator for Base
    Base(Base const& d) { /* Initialise *this from d */ }
    Base const& operator=(Base const& rhs) { /* Copy d to *this */ }
};

Obwohl dies die Menge an Arbeit reduziert erforderlich, mit diesem Ansatz, den Sie noch mit jeder Basisklasse, um zu verwirren müssen sie zu schützen. Auch wird es zu Problemen führen, wenn es legitime Konvertierungen von Base zu SomeOtherClass, die ein operator Base() Mitglied SomeOtherClass beschäftigen. (In diesem Fall wird eine aufwendigere Lösung boost::disable_if<is_same<T, SomeOtherClass> > Beteiligung verwendet werden kann.) Auf jeden Fall sollten Sie diesen Code entfernen, sobald Sie alle Instanzen des Objekts Slicing identifiziert haben.

Zu den Compiler Implementierer der Welt: Prüfung für das Objekt Slicing ist definitiv etwas, das lohnt schaffen (optional) Warnungen für sein würde! Ich kann nicht glauben, von einem Fall, wo es Verhalten wünschenswert wäre, und es ist sehr häufig in newbie C ++ Code zu sehen.

[EDIT 27/3/2015:] Wie von Matt McNab wies darauf hin, Sie brauchen eigentlich nicht den Kopierkonstruktor und Zuweisungsoperator ausdrücklich zu erklären, wie ich oben getan haben, als sie noch implizit vom Compiler deklariert werden. In 2003 C ++ Standard wird dies ausdrücklich in Fußnote erwähnt 106 unter 12,8 / 2:

  

Da ein Template-Konstruktor nie eine Kopie Konstruktor ist, wird das Vorhandensein einer solchen Vorlage nicht die implizite Deklaration eines Copykonstruktor unterdrücken. Template Konstrukteuren mit bei Überlastung Auflösung mit anderen Konstrukteuren, einschließlich Kopierkonstruktoren und ein Template-Konstruktor verwendet werden kann, um ein Objekt zu kopieren, wenn es eine bessere Übereinstimmung als andere Konstrukteure zur Verfügung stellt.

Ich würde vorschlagen, einen Konstruktor Ihre Basisklasse hinzugefügt, die explizit eine konstante Referenz auf die abgeleitete Klasse nimmt (mit einer Vorwärts-Erklärung). In meinem einfachen Test app, wird dieser Konstruktor im Slicing-Fall genannt. Sie könnten dann zumindest eine Laufzeit Behauptung, und Sie könnten wahrscheinlich eine Kompilierung-Behauptung mit geschicktem Einsatz von Vorlagen erhalten (zB: instanziiert eine Vorlage in eine Weise, die eine Compiler-Behauptung in diesem Konstruktor erzeugt). Es kann auch Compiler-spezifische Möglichkeiten zu bekommen, Zeit Warnungen oder Fehler kompilieren, wenn Sie explizite Funktionen aufrufen; zum Beispiel können Sie „__declspec (veraltet)“ für die „slice Konstruktor“ in Visual Studio bekommen eine Compiler-Warnung, zumindest in dem Funktionsaufruf Fall verwenden.

So in Ihrem Beispiel der Code würde wie folgt aussehen (für Visual Studio):

class Base { ...
    __declspec(deprecated) Base( const Derived& oOther )
    {
        // Static assert here if possible...
    }
...

Das funktioniert in meinem Test (Compiler-Warnung). Beachten Sie, dass es nicht die Kopie Fall nicht lösen, sondern ein ähnlich konstruierte Zuweisungsoperator sollte es dort tun.

Hoffe, das hilft. :)

Dies wird gemeinhin als Slicing und ist ein bekanntes Problem genug zu haben, seine besitzt Wikipedia-Artikel (auch wenn es nur eine kurze Beschreibung des Problems ist).

Ich glaube, ich habe einen Compiler verwendet, die eine Warnung hatte Sie ermöglichen könnte darüber zu erkennen und zu warnen. Aber ich kann mich nicht erinnern, welches das war.

Nicht wirklich eine Lösung für Ihr unmittelbares Problem, aber ....

Die meisten Funktionen, die Klasse zu nehmen / struct Objekte als Parameter die Parameter erklären sollten vom Typ „const X &“ oder „X &“ sein, es sei denn, sie einen sehr guten Grund, nicht zu haben.

Wenn Sie dies immer tun, Objekt Slicing wird nie ein Problem sein (Referenzen nicht bekommen, in Scheiben geschnitten!).

Der beste Weg, um dieses Problem zu bekämpfen, ist in der Regel Scott Meyers Empfehlung (siehe Effective C ++ ) nur mit konkreten Klassen an dem Blattknoten des Vererbungsbaums und sicherzustellen, dass Nicht-Blatt-Klassen werden folgen abstrakt durch mindestens eine rein virtuelle Funktion (den destructor, wenn nichts anderes).

mit

Es ist erstaunlich, wie oft dieser Ansatz das Design auch auf andere Weise hilft zu klären, wie gut. Der Aufwand für eine gemeinsame abstrakte Schnittstelle zu isolieren, ist in der Regel ein lohnendes Design Aufwand in jedem Fall.

Bearbeiten

Obwohl ich ursprünglich nicht dies deutlich machen, ist meine Antwort kommt von der Tatsache, dass es nicht möglich ist, genau über das Objekt schneidet bei der Kompilierung zu warnen, und aus diesem Grunde kann es zu einem falschen Gefühl der Sicherheit führen, wenn Sie eine haben Zeit Behauptung kompilieren oder eine Compiler-Warnung aktiviert. Wenn Sie über Instanzen des Objekts schneiden, um herauszufinden, brauchen und müssen sie korrigieren dann bedeutet es, dass Sie den Wunsch und die Fähigkeit haben, die Legacy-Code zu ändern. Wenn dies der Fall ist, dann glaube ich, dass Sie ernsthaft als eine Möglichkeit, die Klassenhierarchie Refactoring betrachten sollte der Code robuster zu machen.

Meine Argumentation dieser ist.

Betrachten Sie eine externe Bibliothek, die eine Klasse Concrete1 definiert und verwendet sie in der inferface zu dieser Funktion.

void do_something( const Concrete1& c );

der Typ Referenz für Effizienz seines Pass und ist in der Regel eine gute Idee. Wenn die Bibliothek Concrete1 hält ein Wert sein, die Umsetzung entschieden geben kann eine Kopie des Eingangsparameters zu machen.

void do_something( const Concrete1& c )
{
    // ...
    some_storage.push_back( c );
    // ...
}

Wenn der Objekttyp der übergebenen Referenz ist in der Tat Concrete1 und nicht ein anderer abgeleiteten Typ dann dieser Code in Ordnung ist, wird kein Schneiden durchgeführt. Eine allgemeine Warnung an diesem push_back Funktionsaufruf kann nur Fehlalarme produzieren und würde höchstwahrscheinlich nicht hilfreich sein.

einig Client-Code Bedenken Sie, dass Concrete2 von Concrete1 leitet und leitet sie in einer anderen Funktion.

void do_something_else( const Concrete1& c );

Da der Parameter als Referenz genommen wird, tritt kein Aufschneiden hier auf den Parameter zu überprüfen, so wäre es nicht richtig, hier des Schneidens zu warnen, wie es sein kann, dass kein Schneiden auftritt. Passing in einem abgeleiteten Typ zu einer Funktion, die eine gemeinsame und nützliche Art und Weise einen Verweis oder Zeiger nimmt Typen Vorteil von polymorphen zu nehmen, so warnen oder Verbieten dieser kontraproduktiv scheinen.

Also, wo ist es Fehler? Nun, die ‚Fehler‘ ist vorbei in Bezug auf etwas, das von einer Klasse abgeleitet wird, die dann als behandelt wird, obwohl es sich um ein Werttyp durch die gerufene Funktion ist.

Es wird in der Regel keine Möglichkeit, eine konsequent nützliche Kompilierung zu erzeugen Warnung vor Objekt schneiden und deshalb die beste Verteidigung, wo es möglich ist, das Problem durch Design zu beseitigen.

class Derived: public Base{};

Du sagst, Abgeleitet ist eine Basis, und so sollte es in jeder Funktion arbeiten, die eine Basis nimmt. Wenn dies ein echtes Problem ist, vielleicht Vererbung ist nicht das, was Sie wirklich wollen, werden.

Ich änderte den Code leicht:

class Base{
  public:
    Base() {}
    explicit Base(const Base &) {}
};

class Derived: public Base {};

void Func(Base)
{

}

//void Func(Derived)
//{
//
//}

//main
int main() {
  Func(Derived());
}

Die explizite Schlüsselwort wird sicherstellen, dass der Konstruktor wird nicht als implizite Konvertierung Operator verwendet werden. - Sie haben es zu nennen explizit, wenn Sie es verwenden möchten

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top