Zmiana poziomu dostępu odziedziczonego elementu
C++ daje nam możliwość zmiany odziedziczonego poziomu dostępu specyfikator dostępu elementu członkowskiego w klasie pochodnej. Odbywa się to za pomocą za pomocą deklaracji w celu zidentyfikowania członka klasy bazowej (o określonym zakresie), którego dostęp w klasie pochodnej został zmieniony, w ramach nowego specyfikatora dostępu.
Rozważmy na przykład następujący Base:
#include <iostream>
class Base
{
private:
int m_value {};
public:
Base(int value)
: m_value { value }
{
}
protected:
void printValue() const { std::cout << m_value; }
};Ponieważ Base::printValue() został zadeklarowany jako chroniony, może być wywoływany tylko przez Base lub jego klasy pochodne. Publiczność nie może uzyskać do niego dostępu.
Zdefiniujmy klasę pochodną, która zmienia specyfikator dostępu printValue() na publiczny:
class Derived: public Base
{
public:
Derived(int value)
: Base { value }
{
}
// Base::printValue zostało odziedziczone jako chronione, więc opinia publiczna nie ma dostępu
// Ale zmieniamy ją na publiczną poprzez deklarację using
using Base::printValue; // uwaga: brak nawiasu
};Oznacza to, że ten kod będzie teraz działać:
int main()
{
Derived derived { 7 };
// printValue jest publiczny w Derived, więc wszystko jest w porządku
derived.printValue(); // prints 7
return 0;
}Możesz zmieniać specyfikatory dostępu tylko elementów podstawowych, do których normalnie miałaby dostęp klasa pochodna. Dlatego nigdy nie można zmienić specyfikatora dostępu elementu podstawowego z prywatnego na chroniony lub publiczny, ponieważ klasy pochodne nie mają dostępu do prywatnych elementów klasy bazowej.
Ukrywanie funkcjonalności
W C++ nie można usunąć ani ograniczyć funkcjonalności klasy bazowej inaczej niż poprzez modyfikację kodu źródłowego. Jednakże w klasie pochodnej można ukryć funkcjonalność istniejącą w klasie bazowej, tak aby nie można było uzyskać do niej dostępu poprzez klasę pochodną. Można to zrobić po prostu zmieniając odpowiedni specyfikator dostępu.
Na przykład możemy ustawić element publiczny jako prywatny:
#include <iostream>
class Base
{
public:
int m_value{};
};
class Derived : public Base
{
private:
using Base::m_value;
public:
Derived(int value) : Base { value }
{
}
};
int main()
{
Derived derived{ 7 };
std::cout << derived.m_value; // błąd: m_wartość jest prywatna w Derived
Base& base{ derived };
std::cout << base.m_value; // OK: m_value jest profesorem w Base
return 0;
}To pozwoliło nam wziąć źle zaprojektowaną klasę bazową i hermetyzować jej dane w naszej klasie pochodnej. Alternatywnie, zamiast dziedziczyć publicznie elementy Base i uczynić m_value prywatnym przez zastąpienie jego specyfikatora dostępu, moglibyśmy odziedziczyć Base prywatnie, co spowodowałoby, że w pierwszej kolejności wszystkie elementy Base byłyby dziedziczone prywatnie.
Warto jednak zauważyć, że chociaż m_value jest prywatny w klasie Derived, to nadal jest publiczny w klasie Base. Dlatego enkapsulację m_value w Derived można w dalszym ciągu obalić, rzutując na Base& i uzyskując bezpośredni dostęp do elementu członkowskiego.
Dla zaawansowanych czytelników
Z tego samego powodu, jeśli klasa Base ma publiczną funkcję wirtualną, a klasa Derived zmienia specyfikator dostępu na prywatny, publiczność nadal może uzyskać dostęp do prywatnej funkcji Derived, rzutując obiekt Derived na Base& i wywołując funkcję wirtualną. Kompilator na to pozwoli, ponieważ funkcja jest publiczna w Base. Ponieważ jednak obiekt jest w rzeczywistości pochodną, rozpoznawanie funkcji wirtualnej spowoduje rozpoznanie (i wywołanie) (prywatnej) pochodnej wersji funkcji. Kontrola dostępu nie jest egzekwowana w czasie wykonywania.
#include <iostream>
class A
{
public:
virtual void fun()
{
std::cout << "public A::fun()\n";
}
};
class B : public A
{
private:
virtual void fun()
{
std::cout << "private B::fun()\n";
}
};
int main()
{
B b {};
b.fun(); // błąd kompilacji: niedozwolone, ponieważ B::fun() jest prywatny
static_cast<A&>(b).fun(); // OK: A::fun() jest publiczna, przekształca się w prywatną B::fun() w czasie wykonywania
return 0;
}Być może zaskakujące, biorąc pod uwagę zestaw przeciążonych funkcji w klasie bazowej, nie ma możliwości zmiany specyfikatora dostępu dla pojedynczego przeciążenia. Możesz tylko zmienić je wszystkie:
#include <iostream>
class Base
{
public:
int m_value{};
int getValue() const { return m_value; }
int getValue(int) const { return m_value; }
};
class Derived : public Base
{
private:
using Base::getValue; // uczyń WSZYSTKIE funkcje getValue prywatnymi
public:
Derived(int value) : Base { value }
{
}
};
int main()
{
Derived derived{ 7 };
std::cout << derived.getValue(); // błąd: getValue() jest prywatny w Derived
std::cout << derived.getValue(5); // błąd: getValue(int) jest prywatny w Derived
return 0;
}Usuwanie funkcji w klasie pochodnej
Możesz także oznaczyć funkcje składowe jako usunięte w klasie pochodnej, co gwarantuje, że nie będzie można ich w ogóle wywołać poprzez obiekt pochodny:
#include <iostream>
class Base
{
private:
int m_value {};
public:
Base(int value)
: m_value { value }
{
}
int getValue() const { return m_value; }
};
class Derived : public Base
{
public:
Derived(int value)
: Base { value }
{
}
int getValue() const = delete; // oznacz tę funkcję jako inaccessible
};
int main()
{
Derived derived { 7 };
// Poniższe instrukcje nie zadziałają, ponieważ funkcja getValue() została usunięta!
std::cout << derived.getValue();
return 0;
}W powyższym przykładzie oznaczyliśmy funkcję getValue() jako usuniętą. Oznacza to, że kompilator będzie narzekał, gdy spróbujemy wywołać pochodną wersję funkcji. Należy jednak pamiętać, że podstawowa wersja getValue() jest nadal dostępna. Możemy wywołać Base::getValue() na jeden z dwóch sposobów:
int main()
{
Derived derived { 7 };
// Możemy wywołać funkcję Base::getValue() bezpośrednio
std::cout << derived.Base::getValue();
// Możemy też przenieść Derived do referencji Base, a funkcja getValue() przekształci się w Base::getValue()
std::cout << static_cast<Base&>(derived).getValue();
return 0;
}Jeśli używasz metody rzutowania, rzutujemy na Base&, a nie na Base, aby uniknąć tworzenia kopii części Base z derived.

