Zoeken…


Syntaxis

  • // Bellen:

    • variable.member_function ();
    • variable_pointer-> member_function ();
  • // Definitie:

    • ret_type class_name :: member_function () cv-qualifiers {
      • lichaam;
    • }
  • // Prototype:

    • class class_name {
      • virt-specifier ret_type member_function () cv-qualifiers virt-specifier-seq;
      • // virt-specifier: "virtual", indien van toepassing.
      • // cv-kwalificaties: "const" en / of "vluchtig", indien van toepassing.
      • // virt-specifier-seq: "override" en / of "final", indien van toepassing.
    • }

Opmerkingen

Een niet- static lidfunctie is een class / struct / union struct die op een bepaalde instantie wordt aangeroepen en op die instantie werkt. In tegenstelling tot static lidfuncties, kan deze niet worden aangeroepen zonder een instantie op te geven.

Zie het hoofdonderwerp voor informatie over klassen, structuren en vakbonden.

Niet-statische ledenfuncties

Een class of struct kan zowel struct als lidvariabelen hebben. Deze functies hebben een syntaxis die meestal vergelijkbaar is met zelfstandige functies en die binnen of buiten de klassedefinitie kunnen worden gedefinieerd; indien gedefinieerd buiten de klassedefinitie, wordt de naam van de functie voorafgegaan door de naam van de klasse en de operator scope ( :: :).

class CL {
  public:
    void  definedInside() {}
    void definedOutside();
};
void CL::definedOutside() {}

Deze functies worden op een instantie (of verwijzing naar een instantie) van de klasse met de punt ( . ) Operator genoemd, of een aanwijzer naar een instantie met de pijl ( -> ) operator, en elke oproep is gekoppeld aan de instantie de functie werd opgeroepen; wanneer een lidfunctie wordt aangeroepen voor een instantie, heeft deze toegang tot alle velden van die instantie (via this aanwijzer ), maar heeft hij alleen toegang tot de velden van andere instanties als die instanties worden geleverd als parameters.

struct ST {
    ST(const std::string& ss = "Wolf", int ii = 359) : s(ss), i(ii) { }

    int get_i() const { return i; }
    bool compare_i(const ST& other) const { return (i == other.i); }

  private:
    std::string s;
    int i;
};
ST st1;
ST st2("Species", 8472);

int  i = st1.get_i(); // Can access st1.i, but not st2.i.
bool b = st1.compare_i(st2); // Can access st1 & st2.

Deze functies hebben toegang tot lidvariabelen en / of andere lidfuncties, ongeacht de toegangsmodificatoren van de variabele of de functie. Ze kunnen ook buiten de orde worden geschreven, toegang krijgen tot lidvariabelen en / of aanroepende lidfuncties die daarvoor zijn gedeclareerd, omdat de volledige klassedefinitie moet worden ontleed voordat de compiler kan beginnen met het compileren van een klasse.

class Access {
  public:
    Access(int i_ = 8088, int j_ = 8086, int k_ = 6502) : i(i_), j(j_), k(k_) {}

    int i;
    int get_k() const { return k; }
    bool private_no_more() const { return i_be_private(); }
  protected:
    int j;
    int get_i() const { return i; }
  private:
    int k;
    int get_j() const { return j; }
    bool i_be_private() const { return ((i > j) && (k < j)); }
};

inkapseling

Een algemeen gebruik van lidfuncties is voor inkapseling, met behulp van een accessor (algemeen bekend als een getter) en een mutator (algemeen bekend als een setter) in plaats van rechtstreeks toegang te krijgen tot velden.

class Encapsulator {
    int encapsulated;

  public:
    int  get_encapsulated() const { return encapsulated; }
    void set_encapsulated(int e)  { encapsulated = e; }

    void some_func() {
        do_something_with(encapsulated);
    }
};

Binnen de klasse is encapsulated vrij toegankelijk voor elke niet-statische lidfunctie; buiten de klasse wordt de toegang get_encapsulated() geregeld door get_encapsulated() , met behulp van get_encapsulated() om het te lezen en set_encapsulated() om het te wijzigen. Dit voorkomt onbedoelde wijzigingen van de variabele, omdat afzonderlijke functies worden gebruikt om deze te lezen en te schrijven. [Er zijn veel discussies over of getters en setters encapsulation bieden of breken, met goede argumenten voor beide claims; een dergelijk verhit debat valt buiten het bestek van dit voorbeeld.]

Naam verbergen en importeren

Wanneer een basisklasse een set overbelaste functies biedt en een afgeleide klasse nog een overbelasting aan de set toevoegt, worden alle overbelastingen van de basisklasse verborgen.

struct HiddenBase {
    void f(int) { std::cout << "int" << std::endl; }
    void f(bool) { std::cout << "bool" << std::endl; }
    void f(std::string) { std::cout << "std::string" << std::endl; }
};

struct HidingDerived : HiddenBase {
    void f(float) { std::cout << "float" << std::endl; }
};

// ...

HiddenBase hb;
HidingDerived hd;
std::string s;

hb.f(1);    // Output:  int
hb.f(true); // Output:  bool
hb.f(s);    // Output:  std::string;

hd.f(1.f);  // Output:  float
hd.f(3);    // Output:  float
hd.f(true); // Output:  float
hd.f(s);    // Error: Can't convert from std::string to float.

Dit is te wijten aan regels voor naamomzetting: tijdens het opzoeken van de naam stoppen we met zoeken, zelfs als we duidelijk niet de juiste versie van de entiteit met die naam hebben gevonden (zoals bij hd.f(s) ); hierdoor voorkomt het overbelasten van de functie in de afgeleide klasse dat het opzoeken van namen de overbelastingen in de basisklasse ontdekt. Om dit te voorkomen, kan een gebruikendeclaratie worden gebruikt om namen uit de basisklasse in de afgeleide klasse te "importeren", zodat ze beschikbaar zijn tijdens het opzoeken van de naam.

struct HidingDerived : HiddenBase {
     // All members named HiddenBase::f shall be considered members of HidingDerived for lookup.
    using HiddenBase::f;

    void f(float) { std::cout << "float" << std::endl; }
};

// ...

HidingDerived hd;

hd.f(1.f);  // Output:  float
hd.f(3);    // Output:  int
hd.f(true); // Output:  bool
hd.f(s);    // Output:  std::string

Als een afgeleide klasse namen met een gebruikendeclaratie importeert, maar ook functies declareert met dezelfde handtekening als functies in de basisklasse, worden de basisklassefuncties stilzwijgend genegeerd of verborgen.

struct NamesHidden {
    virtual void hide_me()      {}
    virtual void hide_me(float) {}
    void hide_me(int)           {}
    void hide_me(bool)          {}
};

struct NameHider : NamesHidden {
    using NamesHidden::hide_me;

    void hide_me()    {} // Overrides NamesHidden::hide_me().
    void hide_me(int) {} // Hides NamesHidden::hide_me(int).
};

Een gebruikendeclaratie kan ook worden gebruikt om toegangsmodificaties te wijzigen, op voorwaarde dat de geïmporteerde entiteit public of protected in de basisklasse.

struct ProMem {
  protected:
    void func() {}
};

struct BecomesPub : ProMem {
    using ProMem::func;
};

// ...

ProMem pm;
BecomesPub bp;

pm.func(); // Error: protected.
bp.func(); // Good.

Evenzo, als we expliciet een lidfunctie uit een specifieke klasse in de overervingshiërarchie willen aanroepen, kunnen we de functienaam kwalificeren bij het aanroepen van de functie, door die klasse op naam op te geven.

struct One {
    virtual void f() { std::cout << "One." << std::endl; }
};

struct Two : One {
    void f() override {
        One::f(); // this->One::f();
        std::cout << "Two." << std::endl;
    }
};

struct Three : Two {
    void f() override {
        Two::f(); // this->Two::f();
        std::cout << "Three." << std::endl;
    }
};

// ...

Three t;

t.f();      // Normal syntax.
t.Two::f(); // Calls version of f() defined in Two.
t.One::f(); // Calls version of f() defined in One.

Virtuele ledenfuncties

Ledenfuncties kunnen ook virtual worden verklaard. In dit geval zullen deze niet direct toegankelijk zijn als ze op een aanwijzer of verwijzing naar een instantie worden aangeroepen; in plaats daarvan zullen ze de functie opzoeken in de virtuele functietabel (een lijst met verwijzingen naar lid-functies voor virtuele functies, beter bekend als de vtable of vftable ), en die gebruiken om de versie aan te roepen die geschikt is voor de dynamische dynamiek van de instantie (feitelijk) type. Als de functie rechtstreeks wordt aangeroepen vanuit een variabele van een klasse, wordt er niet opgezocht.

struct Base {
    virtual void func() { std::cout << "In Base." << std::endl; }
};

struct Derived : Base {
    void func() override { std::cout << "In Derived." << std::endl; }
};

void slicer(Base x) { x.func(); }

// ...

Base b;
Derived d;

Base *pb = &b, *pd = &d; // Pointers.
Base &rb = b, &rd = d;   // References.

b.func();   // Output:  In Base.
d.func();   // Output:  In Derived.

pb->func(); // Output:  In Base.
pd->func(); // Output:  In Derived.

rb.func();  // Output:  In Base.
rd.func();  // Output:  In Derived.

slicer(b);  // Output:  In Base.
slicer(d);  // Output:  In Base.

Merk op dat, terwijl pd Base* , en rd Base& , func() aanroept op een van de twee aanroepen Derived::func() plaats van Base::func() ; dit komt omdat de vtable voor Derived het vtable Base::func() bijwerkt om in plaats daarvan te verwijzen naar Derived::func() . Omgekeerd, merk op hoe het doorgeven van een instantie aan slicer() altijd toe leidt dat Base::func() wordt aangeroepen, zelfs wanneer de doorgegeven instantie een Derived ; dit komt door iets dat bekend staat als gegevenssegmentering , waarbij het door waarde doorgeven van een Derived instantie in een parameter Base op waarde het deel van de Derived instantie dat geen Base instantie is, ontoegankelijk maakt.

Wanneer een lidfunctie is gedefinieerd als virtueel, hebben alle afgeleide klassenlidfuncties met dezelfde handtekening voorrang, ongeacht of de vervangende functie is opgegeven als virtual of niet. Dit kan afgeleide klassen echter moeilijker maken voor programmeurs om te parseren, omdat er geen indicatie is welke functie (s) virtual is / zijn.

struct B {
    virtual void f() {}
};

struct D : B {
    void f() {} // Implicitly virtual, overrides B::f.
                //  You'd have to check B to know that, though.
};

Merk echter op dat een afgeleide functie alleen een basisfunctie overschrijft als hun handtekeningen overeenkomen; zelfs als een afgeleide functie expliciet virtual wordt verklaard, zal deze in plaats daarvan een nieuwe virtuele functie maken als de handtekeningen niet overeenkomen.

struct BadB {
    virtual void f() {}
};

struct BadD : BadB {
    virtual void f(int i) {} // Does NOT override BadB::f.
};
C ++ 11

Vanaf C ++ 11 kan de intentie om te negeren expliciet worden gemaakt met het contextgevoelige sleutelwoord override . Dit vertelt de compiler dat het programmeerapparaat verwacht dat het een base class-functie overschrijft, waardoor de compiler een fout overslaat als deze niets overschrijft.

struct CPP11B {
    virtual void f() {}
};

struct CPP11D : CPP11B {
    void f() override {}
    void f(int i) override {} // Error: Doesn't actually override anything.
};

Dit heeft ook het voordeel dat programmeurs worden verteld dat de functie zowel virtueel is als ook in ten minste één basisklasse wordt gedeclareerd, waardoor complexe klassen gemakkelijker kunnen worden ontleed.

Wanneer een functie virtual wordt verklaard en buiten de klassedefinitie wordt gedefinieerd, moet de virtual specificatie worden opgenomen in de functieverklaring en niet worden herhaald in de definitie.

C ++ 11

Dit geldt ook voor override .

struct VB {
    virtual void f(); // "virtual" goes here.
    void g();
};
/* virtual */ void VB::f() {} // Not here.
virtual void VB::g() {} // Error.

Als een basisklasse een virtual functie overbelast, zijn alleen overbelastingen die expliciet als virtual zijn gespecificeerd, virtueel.

struct BOverload {
    virtual void func() {}
    void func(int) {}
};

struct DOverload : BOverload {
    void func() override {}
    void func(int) {}
};

// ...

BOverload* bo = new DOverload;
bo->func(); // Calls DOverload::func().
bo->func(1); // Calls BOverload::func(int).

Zie het relevante onderwerp voor meer informatie.

Const Correctheid

Een van de belangrijkste toepassingen voor this cv-kwalificaties is const correctheid . Dit is de praktijk te garanderen dat alleen toegangen noodzaak om een object aan te passen zijn in staat om het object te wijzigen, en dat eventuele (lid of niet-lid) functie die niet hoeft om een object te wijzigen heeft geen schrijftoegang tot die object (direct of indirect). Dit voorkomt onbedoelde wijzigingen, waardoor code minder foutgevoelig wordt. Het staat ook elke functie toe die de status niet hoeft te wijzigen om een const of niet- const object te kunnen nemen, zonder de functie te moeten herschrijven of overbelasten.

const correctheid, const vanwege zijn aard, begint van onderaf: elke functie van een lid van een klasse die de status niet hoeft te veranderen, wordt als const verklaard , zodat deze op instanties van const kan worden opgeroepen. Dit op zijn beurt maakt doorgegeven door verwijzing parameters worden verklaard const als ze niet te worden gewijzigd, waardoor functies ofwel const of niet- const objecten zonder klagen en const ness naar buiten kan voortplanten in deze manier. Hierdoor zijn getters vaak const , net als andere functies die de logische status niet hoeven te wijzigen.

class ConstIncorrect {
    Field fld;

  public:
    ConstIncorrect(const Field& f) : fld(f) {}     // Modifies.

    const Field& get_field()       { return fld; } // Doesn't modify; should be const.
    void set_field(const Field& f) { fld = f; }    // Modifies.

    void do_something(int i) {                     // Modifies.
        fld.insert_value(i);
    }
    void do_nothing()        { }                   // Doesn't modify; should be const.
};

class ConstCorrect {
    Field fld;

  public:
    ConstCorrect(const Field& f) : fld(f) {}       // Not const: Modifies.

    const Field& get_field() const { return fld; } // const: Doesn't modify.
    void set_field(const Field& f) { fld = f; }    // Not const: Modifies.

    void do_something(int i) {                     // Not const: Modifies.
        fld.insert_value(i);
    }
    void do_nothing() const  { }                   // const: Doesn't modify.
};

// ...

const ConstIncorrect i_cant_do_anything(make_me_a_field());
// Now, let's read it...
Field f = i_cant_do_anything.get_field();
  // Error: Loses cv-qualifiers, get_field() isn't const.
i_cant_do_anything.do_nothing();
  // Error: Same as above.
// Oops.

const ConstCorrect but_i_can(make_me_a_field());
// Now, let's read it...
Field f = but_i_can.get_field(); // Good.
but_i_can.do_nothing();          // Good.

Zoals geïllustreerd door de opmerkingen over ConstIncorrect en ConstCorrect , dienen functies voor cv-kwalificatie ook als documentatie. Als een klasse const correct is, kan van elke functie die niet const is veilig worden aangenomen dat deze van staat verandert, en van elke functie die const kan veilig worden aangenomen dat deze de status niet verandert.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow