2017-05-15 2 views
1

Ich habe heute ein C++ 03 Beispiel online gesehen.Wie erstellt man das erste Objekt, wenn nur der Kopierkonstruktor deklariert ist?

Ich wurde gesagt, dass in diesem Fall kein Standardkonstruktor automatisch vom Compiler generiert wird. Wenn dies der Fall ist, bedeutet dies, dass ein neues Cat-Objekt aus einem vorhandenen Cat-Objekt erstellt werden kann.

Kann mir jemand sagen, wie man das erste Cat-Objekt in diesem Fall erstellen? oder korrigiere mich, wenn mein Verständnis falsch ist.

+5

"Kann mir jemand sagen, wie man das erste Cat-Objekt in diesem Fall erstellt?" - Sie können nicht. Also schreibe keinen Code wie diesen. –

+2

Vielleicht war dieses Beispiel nur zur Veranschaulichung? In vernünftigem Code möchten Sie einen zusätzlichen Konstruktor haben, unabhängig davon, ob es sich um einen Standardkonstruktor handelt oder nicht. –

+0

Ich würde vorschlagen, dass Sie mindestens Ihre Klasse mit einem privaten Konstruktor und einer Funktion erweitern müssen, um eine Basisklasse mit einigen vernünftigen Standardwerten zu initialisieren, wenn Sie nicht möchten, dass sie allgemein konstruierbar ist. – Donnie

Antwort

2

Zunächst einmal sollte die gültige Antwort sein, dass es keine Möglichkeit gibt, die erste Cat zu erstellen. Das Beispiel wurde wahrscheinlich nur herausgezogen, um zu demonstrieren, wie ein vom Benutzer deklarierter Konstruktor verhindert, dass der implizit deklarierte Standardkonstruktor deklariert wird.


nun trotz dieser und rein, wenn die Mittel, um die Enden rechtfertigt, gibt es jedoch Möglichkeiten, die erste Katze die Schaffung eines Layout-kompatibel Objekt.

C++ 11 eingeführt Layoutkompatibilität:

Zwei Standard-Layout-Struktur (Ziffer 9) Typen sind Layout-kompatibel, wenn sie die gleiche Anzahl von nicht-statischen Datenelementen und entsprechenden nicht- statische Datenelemente (in der Deklarationsreihenfolge) haben Layout-kompatible Typen (3.9).


Eine Standard-Layout-Klasse ist eine Klasse, die:

  • Nicht-Standard-Layout-Klasse (oder Anordnung solcher Typen) oder Referenz, keine nicht-statischen Datenelemente vom Typ
  • hat
  • hat keine virtuellen Funktionen (10.3) und keine virtuellen Basisklassen (10.1),
  • die gleiche Zugangskontrolle hat (Abschnitt 11) für alle nicht-statischen Datenelemente,
  • keine nicht-Standard-Layout-Basisklassen aufweisen,
  • hat entweder keine nicht statischen Datenelemente in der abgeleiteten Klasse und höchstens eine Basisklasse mit nicht statischen Datenmembern oder keine Basisklassen mit nicht statischen Datenmembern, und * hat keine Basisklassen gleicher Typ wie das erste nicht statische Datenelement.

Eine Standard-Layout-Struktur ist eine Standard-Layout-Klasse mit dem Klassenschlüssel definiert struct oder dem Klasse-Keys class.

Dies bedeutet, dass Sie tun können:

class Cat { 
public: 
    Cat(const Cat& iCat) : x{iCat.x} {} 
    int x; 
}; 

class foo { 
public: 
    foo(int x) : m_x{x} {} 
    int m_x; 
}; 

int main() { 
    foo f{5}; 
    Cat* c1 = reinterpret_cast<Cat*>(&f); 
    Cat c2 = *c1; 
    std::cout << c2.x << std::endl; // 5 
} 

Die x und m_x Mitglieder das Kopieren von (Layout-kompatibel) Mitglieder zu demonstrieren, werden verwendet.

Hinweis: Wie im Kommentar von @M.M erwähnt, müssen Sie möglicherweise das strikte Aliasing in Ihrem Compiler deaktivieren, damit dies funktioniert.

+1

Dies verletzt die strenge Aliasing-Regel (obwohl die Klassen das gleiche Layout haben) –

+0

@ M.M Hmm, also müssen Sie das strikte Aliasing im Compiler deaktivieren, damit dies funktioniert. Oder vielleicht eine Verbindung irgendwie? – Snps

+0

Ja. Ihre Antwort hat mich dazu inspiriert, einen mit der Regel "common-initial-sequence" zu schreiben, obwohl ich nicht überzeugt bin, dass meine Antwort nicht auch UB ist –

-2

Sie können dies tun, obwohl es gar nicht empfohlen wird; dabei ist unter dem, was ist seit sehr sehr schlecht Praxis und führt zu „undefiniert Verhalten“

const Cat* pCat = nullptr; 
const Cat& cat = *pCat; // yikes! NULL reference...boo! 
Cat fluffy(cat); 

Wenn Sie etwas zu tun, sind versuchen, sind Sie wahrscheinlich nähern sie das Problem in einem völlig falschen Weg und Sie sollten Ihre Lösung überdenken.

+1

Nicht nur ist es nicht empfohlen, aber Sie sind sofort auf Undefined Behaviour Land. –

+2

Ja, lass uns einen Nullzeiger dereferenzieren. Das wird großartig gehen. – Donnie

+0

Sie können "alles" tun, jedoch ist etwas Schlechtes keine Antwort, es sei denn, Sie möchten jemandem, der eine Frage stellt, wirklich schlechte Ideen geben. – Donnie

-2

Führen Sie im Produktionscode nicht die folgenden Schritte aus, aber für den Fall der Illustration wurde mit "gcc 5.4.0" gearbeitet.

Da es UB (undefiniertes Verhalten) gibt, verwenden Sie hier std :: string, z. als Membervariable der Katze, wird das Beispiel auf abgespeckte:

Aus Gründen zu bringen, eine ‚Katze‘ zum Leben, könnte man einig reinterpret-cast tun, um Ihre ‚erste‘ Katze zu erstellen:

class Cat { 
    public: 
     Cat(const Cat& iCat) { 
     } 
}; 

int main() {   
    Cat* cat = reinterpret_cast<Cat*>(new char[sizeof(Cat)]); 
    Cat cat2 = Cat(*cat); 
    delete cat; 
} 

Auch hier ist der obige Code nur zur Veranschaulichung, funktioniert nur auf gcc 5.4.0 und garantiert nicht, dass es mit anderen Compilern funktioniert.

Es besteht die Möglichkeit, auf einfache Weise UB im Code einführen, indem sie sie erweitert, wie in den Kommentaren zum Beispiel erklärt:

#include <iostream> 

class Cat { 
    private: 
     std::string name_; 
    public: 
     Cat(const Cat& iCat) { 
      this->name_ = iCat.name_; 
     } 
     void setName(const std::string& name) { name_ = name; } 
     const std::string& name() { return name_; } 
}; 

int main() {   
    Cat* cat = reinterpret_cast<Cat*>(new char[sizeof(Cat)]); 
    cat->setName("Lilly"); 
    Cat cat2 = Cat(*cat); 
    std::cout << cat2.name() << std::endl; 
    delete cat; 
} 
+2

Das ist nur für undefiniertes Verhalten. –

+0

Undefiniertes Verhalten. –

+0

πάντα ῥεῖ Kannst du erklären, in welcher Aussage genau UB involviert ist? – cwschmidt

5

jemand kann mir sagen, wie das erste Cat-Objekt in diesem Fall erstellen ? oder korrigiere mich, wenn mein Verständnis falsch ist.

Die einzige hier gültige Antwort ist:

Es gibt nur keine Möglichkeit, den Code Sie auf dem Laufenden. Vermutlich haben Sie einige zusätzliche Funktionen verpasst, die Sie mit diesem Beispiel erhalten haben.


Es gibt Möglichkeiten, aber wenn andere Konstruktor private deklariert wird, z.B .:

class Cat { 
    public: 
     Cat(const Cat& iCat); 
     static Cat* CreateCat(const std::string& color) { 
      return new Cat(color); 
     } 
    private: 
     Cat(const std::string& color) 
}; 
+1

Zufällige Downvotes passieren, kein Punkt sucht ein Grund. Es ist für jetzt wieder gut gemacht :). Jedenfalls wollte ich kommentieren, dass der private Konstruktor auch häufig verwendet wird, um das Singleton-Muster zu implementieren. – spectras

1

ausnutzend C++ 14 [class.mem]/18:

Wenn eine Standard-Layout Union enthält zwei oder mehr Standard-Layout-Strukturen, die eine gemeinsame Anfangssequenz teilen, und wenn Das Standard-Layout-Union-Objekt enthält derzeit eine dieser Standard-Layout-Strukturen. Es ist zulässig, den gemeinsamen Anfangsteil von einem von ihnen zu überprüfen. Zwei Standardlayoutstrukturen teilen sich eine gemeinsame Anfangssequenz , wenn entsprechende Member layoutfähige Typen haben und entweder kein Member ein Bitfeld ist oder beide Bitfelder mit der gleichen Breite für eine Folge von einem oder mehreren Anfangselementen sind.

Cat ist Standard-Layout, so dass wir eine Union, die zwei Arten von gemeinsamen Anfangsfolge enthält, machen können.Zwei beliebige Standard-Layout-Klassen ohne Datenelemente erfüllen die Kriterien für eine gemeinsame Anfangssequenz aufweist, so:

class Cat { 
    public: Cat(const Cat& iCat); 
}; 

class Dog { 
    public: Dog(); 
}; 

union CatDog 
{ 
    Dog dog; 
    Cat cat; 
}; 

int main() 
{ 
    CatDog horse{}; 
    Cat cat(horse.cat); 
} 

Hinweis: Der Standard nicht genau definiert die Bedeutung von „inspizieren den gemeinsamen Anfang Teil". Wenn der gemeinsame Anfangsteil mit der Gesamtheit der Struktur übereinstimmt, bedeutet das, dass die gesamte Struktur wie in meinem Code inspiziert werden kann? Ich denke, das ist eine Frage für die Sprachanwälte.

Verwandte Themen