Bystre triki w C++ (1): wykorzystanie Reguły Zera do śledzenia wywołań Wielkiej Piątki

Reguła Zera jest szczególnym przypadkiem Reguły Pojedynczej Odpowiedzialności – jeżeli jeden z członków twojej klasy potrzebuje specjalnego traktowania przez Wielką Piątkę (destruktor, operatory przypisania, konstruktory kopiujące i konstruktory przenoszące), to powinien być umieszczony w osobnej klasie, inaczej twoja klasa robi za dużo.

Tradycyjnie gdy chcemy prześledzić gdzie wołany jest operator przypisania, dopisujemy do klasy ten operator, w którym wypisujemy na standardowe wyjście. To podejście ma wady – przy dopisywaniu trzeba sprawdzać czy to nie zmieni działania istniejącej klasy, oraz wymusza sprawdzenie które operatory istnieją a które nie – w przypadku klas które nie mają operatora przypisania, dopisanie takiego operatora może zmienić działanie kodu. Ale z użyciem Reguły Zera kod mógłby wyglądać tak:

class noisy
{
    noisy& operator=(noisy&&) noexcept { std::cout << "operator=(noisy&&)\n"; }
    noisy& operator=(const noisy&) { std::cout << "operator=(const noisy&)\n"; }
    noisy(const noisy&) { std::cout << "noisy(const noisy&)\n"; }
    noisy(noisy&&) noexcept { std::cout << "noisy(noisy&&)\n"; }
    ~noisy() { std::cout << "~noisy()\n"; }
    noisy() { std::cout << "noisy()\n"; }
};

class moja_klasa
{
    noisy n;
    int a;
    std::vector<int> b;
};

Et Voila. Klasę można uogólnić żeby nazwa klasy określonej w parametrze szablonu także była wypisywana.

template<typename T>
class noisy
{
    noisy& operator=(noisy&&) noexcept { std::cout << "operator=(noisy<" << typeid(T).name() << ">&&)\n"; }
    noisy& operator=(const noisy&) { std::cout << "operator=(const noisy<" << typeid(T).name() << ">&)\n"; }
    noisy(const noisy&) { std::cout << "noisy(const noisy<" << typeid(T).name() << ">&)\n"; }
    noisy(noisy&&) noexcept { std::cout << "noisy(noisy<" << typeid(T).name() << ">&&)\n"; }
    ~noisy() { std::cout << "~noisy<" << typeid(T).name() << ">()\n"; }
    noisy() { std::cout << "noisy<" << typeid(T).name() << ">()\n"; }
};

class moja_klasa
{
    noisy<moja_klasa> n;
    int a;
    std::vector<int> b;
};

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax