2013-02-19 4 views
11

Objective-C-Kategorie-Funktion ermöglicht es dem Programmierer, eine neue Methode hinzuzufügen, die nicht in der ursprünglichen Klassendefinition definiert wurde.Objective-Cs Kategorie-ähnliches Konstrukt oder Technik in C++?

Kann ich ähnliche Funktionalität (Sprachkonstrukt oder irgendeine Technik) auf C++ archivieren?

Hauptproblem ist konsistente Methode aufrufen Syntax (. oder -> Operator).

+0

Ja, Wenn Sie den Zugriff auf die Klassendefinition haben. Nein, sonst. –

+0

Zur Laufzeit erhalten Sie keine Sprachunterstützung dafür. Sie können jedoch z. eine Karte mit Funktionszeigern. Aber das ist dann eher umständlich und fehleranfällig. –

+0

@AlokSave Meinen Sie, den Quellcode direkt zu ändern? Es könnte eine Option sein. – Eonil

Antwort

7

Lassen Sie uns die folgende Klasse betrachten erweitert werden:

struct A { 
    int x, y; 
    A(int x, int y) : x(x), y(y) {} 
}; 

Sie können von dieser Klasse erben oder eine Wrapper-Klasse schreiben, die eine Instanz dieser Klasse enthält. In den meisten Fällen ist die Vererbung der Weg zu gehen, als Wrapper-Klasse ein A nicht ist, aber es wickelt (enthält) einen A.

Mit C++ 11 bewegen Semantik, eine Instanz zu fördern A zu eine Unterklasse B (vererben A) effizient sein und erfordert nicht die Instanz A zu kopieren:

class B : public A { 
public: 
    B (A &&a) : A(a), someOtherMember(a.x + a.y) {} 

    // added public stuff: 
    int someOtherFunction() const { return someOtherMember; } 

private: 
    // added private stuff: 
    int someOtherMember; 
}; 

Voll Code mit Beispiel: http://ideone.com/mZLLEu

natürlich ist die Funktion, die ich hinzufügen ed ist ein bisschen albern (und das Mitglied noch mehr, da es weitere Änderungen der ursprünglichen Mitglieder x und y nicht respektiert), aber Sie sollten eine Vorstellung davon bekommen, was ich demonstrieren möchte.

Beachte den Konstruktor B (A &&a) was ich "promotion constructor" nenne (dies ist kein Standardbegriff). Normalerweise ist B (B &&b) ein Move-Konstruktor, der verschiebt den Inhalt der bereitgestellten B Instanz in eine neue B über gebaut werden. Ich benutze die Bewegung Semantik zu Bewegung eine Instanz von A (die von einer anderen Funktion zurückgegeben wurde) in der Super-Klasse A von B.

Effektiv können Sie A zu B fördern, während es möglich ist, eine B als A zu verwenden.

Im Gegensatz zu Soontts 'Antwort funktioniert meine Lösung auch mit hinzugefügten virtuellen Tabellen, da sie nicht auf unsicheren Pointer-Casting angewiesen ist.

+0

Ich habe verstanden, dass * move ctor * ein neues Objekt semantisch erzeugt und der Compiler das vorhandene Objekt wenn möglich wiederverwenden kann. Und Ihr * promote ctor * hat auch die gleiche Chance für die Compiler-Optimierung. Ist mein Verständnis richtig? – Eonil

+1

'A (A && a)' bewegt sich ctor für 'A'. Dadurch wird ein altes A in ein neues A verschoben. Semantisch ist es ein neues Objekt, aber dank der Bewegungssemantik kann es den Inhalt des alten 'A'" stehlen ". Die Idee meines Ansatzes ist es, das alte 'A' in ein' B' zu verschieben. Da "B" von "A" erbt, muss es sowieso ein "A" initialisieren.Ich benutze den move ctor von 'A' in einem Konstruktor von' B', den ich nennen möchte * promote ctor * (Ich habe dieses Wort gemacht, da es effektiv ein existierendes 'A' zu einem' B' fördert, ohne den Inhalt zu berühren von 'A' solange seine Bewegung gut geschrieben ist). – leemes

+0

Es gibt eine Feinheit hier - betrachten Sie den Fall, in dem Sie auch z. 'Klasse C: public A', mit einem Move-Konstruktor aus' A'. In diesem Fall ist das Objekt, das Sie haben, entweder ein 'A', ein' B' oder ein 'C'. Es kann nicht gleichzeitig "B" und "C" sein. – alastair

1

C++ hat die Vererbung dafür. Darüber hinaus habe ich die folgenden Trick, ein paar Mal benutzt Klassen von #import erzeugt zu erweitern „progid: ...“ Richtlinie:

// This one is part of external framework, or auto-generated, or dllimport, or #import, etc.. 
class A 
{ 
    protected double m_x; 
}; 

// This one is the extension class. Make sure you only add non-virtual methods here. 
// Static methods and static data members are OK as well. 
class __declspec(novtable) B: public A 
{ 
public: 
    double getSquare(){ return m_x * m_x; } 
    __declspec(property(get = getSquare)) double square; 
}; 

// Usage example 
double someFunc(A& arg) 
{ 
    B& b = static_cast<B&>(arg); // Note we've not constructed any instance of B, just casted. 
    return b.square; 
} 
+0

Oh, eigentlich habe ich gerade eine ähnliche (vielleicht gleiche?) Technik gefunden und eine neue [Frage] geschrieben (http://stackoverflow.com/questions/14952504/is-it-possible-to-downcast-an-object-to) -a-Unterklasse-which-definiert nicht-extra-v). Kann ich diese Technik standardmäßig behandeln? – Eonil

+5

'__declspec' und' property' sind nicht Teil des ISO-Standards C++. –

+0

Ich bin mir absolut sicher, dass es in Visual Studio 2005 und 2008 funktioniert hat. Ich weiß nicht, ob es Standard ist, und um ehrlich zu sein ist mir das egal. "Standard C++" ist nur eine weitere undichte Abstraktion. Um sicherzustellen, dass Ihr C++ Code mit den Compilern A, B und C kompiliert, müssen Sie es entwickeln und testen. – Soonts

5

Eine andere Option, die von einigen nicht als "sauber" angesehen wird (obwohl es meiner Meinung nach), aber immer noch die gleiche Sache erreicht, ist die Verwendung einer statischen Klasse. Es ist wichtig, sich daran zu erinnern, dass bei der Erstellung einer Member-Funktion wirklich hinter den Kulissen passiert, dass die Kompilierung eine Funktion generiert, bei der das Objekt (auch "das" genannt) der erste Parameter ist. Also können wir das Gleiche tun, um die Funktionalität unserer Klasse zu erweitern, ohne daraus abzuleiten.

class Something 
{ 
public: 
    Something() 
    ~Something() 
} 

// In objective-c you may call this category Something+Utils 
class SomethingUtils 
{ 
    // You can use a pointer, or a reference here, your call. 
    static int GetSomethingElse(Something *something, int parameter); 
} 

Dies wird die gleiche Absicht als eine Kategorie erreichen: Sie können die Funktionalität Ihres Klassenobjekt erweitern, und Sie nicht über eine neue abgeleitete Klasse erstellen müssen. Sie werden nicht in der Lage sein, auf private oder geschützte Member-Funktionen und -Variablen zuzugreifen, aber Sie können dies in objective-c sowieso nicht tun, so dass nichts verloren geht (und wenn Sie versuchen, privaten oder geschützten Mitgliedsstaat zu verwenden) habe den Punkt der Kategorien völlig verpasst. Du könntest das nicht benutzen. und -> Operatoren, aber meiner Meinung nach ist das ein viel besserer Kompromiss, als einen neuen Typ abzuleiten, nur um einige Hilfsmethoden hinzuzufügen.

+0

Das ist syntaktisch hässlich, aber ich muss zustimmen, dass dies die praktischste Option ist. Selbst wenn ich die Lösung von @leemes anpasse, würde ich so eine Utility-Klasse schreiben und sie einpacken. – Eonil

+5

Eine leichte Variante, die in mancher Hinsicht eher stilistisch ansprechend ist, ist die Verwendung eines Namensraums. –

+0

Ich würde auch hinzufügen, Eonil, dass, während Sie es als syntaktisch hässlich (ich persönlich nicht), es syntaktisch klar ist. Es ist implizit offensichtlich, dass dies eine Klassenerweiterung und keine Kernmethode ist, ein Problem, unter dem obj-c-Kategorien leiden (Sie müssen der Methode zur Header-Datei folgen, um zu erkennen, dass es sich um eine Kategorie handelt). Was die Hässlichkeit anbelangt, werde ich das nicht diskutieren, weil wir wahrscheinlich niemals eins auf einmal sehen werden :) –

0

Ich habe eine fast konsistente Aufrufkonvention mit an idea I talked about in a lightning talk einem Jahr oder so vor:

(das Intro ohne sie Kommentar weniger Sinn macht - warten, bis es auf das Bit C++ wird).

Beachten Sie, dass das Material nicht ernst genommen werden sollte - obwohl einige zwangsläufig haben ;-)