Wywoływanie konstruktora domyślnego klasy pierwotnej przez klasę potomną C++

0

Witam,
Piszę dla siebie program w języku C++, aby poznać podstawy programowania obiektowego, nauczyć się C++ i przy okazji programować matematyczne aplikacje. Wybrałem macierze, ponieważ jest to okazja, aby wykorzystać podstawową wiedzę z algebry, przy okazji możliwość dziedziczenia (część funkcji dla podklasy kwadratowa nie będzie działać dla klasy ogólnej).

  1. Nie rozumiem dlaczego konstruktory klasy potomnej wywołują konstruktor domyślny klasy bazowej.

Czy jest to domyślne zachowanie C++ i trzeba je mieć na uwadze przy projektowaniu klas? A może to błąd w moim kodzie?

Napisałem prosty program by zasymulować problem:

#include<iostream>

using namespace std;

class macierz {
public:
  macierz();
};

class kwadratowa : public macierz {
};

macierz::macierz() {
  cout << "Wywolano konstruktor domyslny klasy pierwotnej!" << endl;
}

int main(void)
{
  kwadratowa a;
  return 0;
}

Konstuktor domyślny klasy pierwotnej został wywołany, wydaje mi się więc, że tak powinno być. Mam więc pytanie:

  1. Czy można sprawić, by klasa potomna nie wywoływała domyślnego konstruktora klasy pierwotnej?
  2. A może klasy powinny być projektowane zawsze w taki sposób, aby dostosować się do tego zachowania?

Pozdrawiam,

0

Konstruktor klasy bazowej jest zawsze wywoływany(przed konstruktorem klasy potomnej). Ma to swoje konkretne uzasadnienie - przecież to co zostało odziedziczone też musi zostać skonstruowane i właśnie za to odpowiada wywołanie konstruktora klasy bazowej. Możesz co najwyżej wskazać, który konstruktor klasy bazowej zostanie wywołany. Robi się to z poziomu konstruktora klasy potomnej na jego liście inicjalizacyjnej.

Pamiętaj, że klasa ZAWSZE ma konstruktor(nawet jak go explicite nie zdefiniujesz).

0

Dzięki.

0

dodam, że identycznie ma się sprawa z destruktorami podczas dziedziczenia, jednak aby mechanizm ten działał jak należy i zostały wywołane wszystkie destruktory odpowiednich klas to destruktor w klasie bazowej powinien być wirtualny.

0

Ad. 1. Nawet jeśli można by było to było by to bardzo niestosowne
Ad. 2. I tak i nie. Możesz albo pozostać przy samych konstruktorach i się dostosowywać, albo zastosować wzorzec "init" i zrobić tak jak chcesz:

#include<iostream>
 
using namespace std;
 
class macierz {
public:
  macierz();
  virtual void init();
};
 
class kwadratowa : public macierz {
public:
  virtual void init();
};
 
macierz::macierz() {
  // inicjalizacja zmiennych bez przetwarzania
}

void macierz::init() {
  cout << "Inicjalizacja macierzy - klasy pierwotnej!" << endl;
}
 
void kwadratowa::init() {
  macierz::init();
  cout << "Inicjalizacja macierzy kwadratowej" << endl;
}

int main(void)
{
  kwadratowa a;
  a.init();
  return 0;
}

Zalety tego wzorca:

  • możesz stosować wyjątki w procesie inicjalizacji klasy
  • możesz wykorzystywać funkcje wirtualne
  • masz wpływ na kolejność inicjalizacji

Wada:

  • wymagane dodatkowe wywołanie funkcji

0 użytkowników online, w tym zalogowanych: 0, gości: 0