Przeciążnie konstruktorów w klasie, gdy mamy pola typu konst.

0

Witam. Zaczynam się uczyć podstaw programowania obiektowego. Po poznaniu konstruktorów zacząłem czytać sobie o liście inicjalizacyjnej, która jest rozszerzeniem możliwości zwykłego konstruktora. Jej zadaniem jest inicjalizacja składowych nowego obiektu. Ważnym jest fakt, że wykonuje się ona jeszcze zanim obiekt zacznie istnieć. Czyli m.in. można dzięki niej przypisać wartość do pól w klasie, które są zadeklarowane jako const. Napisałem z przykładu z internetu taki program:

#include <iostream>
#include <string>

using namespace std;

class Osoba {
public:
    const int wiek;
    const int wzrost;
    string imie;             
    Osoba(int wk, int WZROST, string IMIE) : wiek(wk), wzrost(WZROST) { 
        this->imie = IMIE; 
    }
};


int main()
{

    Osoba Karol(24,185,"Karol"); //powyzej jest konst. wieloargumentowy, wiec musimy podac argumenty 
    //przy tworzeniu obiektu klasy

    cout<<Karol.wiek<<endl; //wypisanie elementow 
    cout<<Karol.wzrost<<endl;
    cout<<Karol.imie<<endl;

    return 0;
}

Tutaj wszystko rozumiem. Ale w poprzednim artykule ze strony, z której się uczyłem, przeczytałem o przeciążeniu konstruktorów (coś podobnego do przeciążenia funkcji). Jeżeli użyjemy w klasie tylko jednego konstruktora i będzie to np. konstruktor wieloargumentowy, to zawsze podczas stworzenia obiektu klasy będziemy musieli podać argumenty. Jeżeli przeciążymy konstruktory i zrobimy drugi konstruktor domyślny bez parametrów, to będziemy mieli wybór, czy podczas stworzenia obiektu klasy podajemy argumenty, czy nie. W powyższym programie chciałem właśnie przeciążyć konstruktor, tworzący drugi konstruktor domyślny bez parametrów, abym nie musiał przy każdym nowym obiekcie klasy Osoba podawać danych. Napisałem coś takiego:

#include <iostream>
#include <string>

using namespace std;

class Osoba {
public:
    const int wiek;
    const int wzrost;
    string imie;             
    Osoba(int wk, int WZROST, string IMIE) : wiek(wk), wzrost(WZROST) { 
        this->imie = IMIE; 
    }
  Osoba() //konstruktor domyslny - blad kompilacji
    {

    }
};


int main()
{

    Osoba Karol(24,185,"Karol"); //powyzej jest konst. wieloargumentowy, wiec musimy podac argumenty 
    //przy tworzeniu obiektu klasy

    cout<<Karol.wiek<<endl; //wypisanie elementow 
    cout<<Karol.wzrost<<endl;
    cout<<Karol.imie<<endl;

   Osoba Karol2(); //tu miał być nowy obiekt klasy Osoba, bez podanych argumentow podczas tworzenia, 
  //co miało byc mozliwie dzieki przeciazeniu konstruktorow

    return 0;
}

IDE pokazuje mi, że błąd jest w zmiennej const, czyli mam rozumieć, ze gdy w polach klasy mamy zmienne typu const, to musimy użyć konstruktora wieloargumentowego z listą inicjalizacyjną i nie możemy przeciążyć go, doając nowy konstruktor bezargumentowy?

I jeszcze mam jedno pytanie odnośnie nazewnictwa: Jak stworzyłem powyżej klasę Osoba, to mówię o tym: "to jest klasa osoba", tak?. A jak potem napisałem:

Osoba Karol(24,185,"Karol"); 

To mówię wtedy, że "stworzyłem obiekt danych typu Osoba"? Czy też można powiedzieć, że jest to po prostu klasa Osoba?

2

Musisz zainicjalizować pole const. Użyj do tego listy inicjalizacyjnej konstruktora:

Osoba() : wiek{42}, wzrost{190} {}
2

IDE pokazuje mi, że błąd jest w zmiennej const, czyli mam rozumieć, ze gdy w polach klasy mamy zmienne typu const, to musimy użyć konstruktora wieloargumentowego z listą inicjalizacyjną i nie możemy przeciążyć go, doając nowy konstruktor bezargumentowy?

Nie. Natomiast każde pole const musi być zainicjalizowane albo bezpośrednio (const int wiek {18}), albo listą inicjalizującą konstruktora (Osoba(): wiek(18), wzrost(160)).

0

Ok, rozumiem, to możecie mi jeszcze odpowiedzieć na moje drugie pytanie, odnośnie nazewnictwa klas?

2

Karol jest obiektem typu Osoba. Osoba to klasa, a nie jej konkretna instancja.

0

Mam kolejny problem. Przeciążyłem konstruktor tak, aby można było podać tylko wartość pola imie, lub nie podawać w ogóle wartości. Napisałem coś takiego:

#include <iostream>
#include <string>

using namespace std;

class Osoba {
public:
    const int wiek;
    const int wzrost;
    string imie;

    Osoba(int wk, int WZROST, string IMIE) : wiek(wk), wzrost(WZROST)
    { 
        this->imie = IMIE;
    }
    
    Osoba() : wiek(42), wzrost(190) 
    { 

    }
    Osoba(string imie) : wiek(18), wzrost(160)
    {
      this->imie = imie;
    }
};


int main()
{

    Osoba Karol(24,185,"Karol"); 

    cout<<Karol.wiek<<endl;
    cout<<Karol.wzrost<<endl;
    cout<<Karol.imie<<endl<<endl;

    Osoba Karol2(); //blad kompilacji - powinno byc Karol2;

    cout<<Karol2.wiek<<endl; //w tym momencie pojawia się błąd kompilacji. Jezeli wyrzuce trzy ponizsze couty, to program zadziala
    cout<<Karol2.wzrost<<endl;
    cout<<Karol2.imie<<endl<<endl;

    Osoba Karol3("abcd"); 

    cout<<Karol3.wiek<<endl;
    cout<<Karol3.wzrost<<endl;
    cout<<Karol3.imie<<endl<<endl;

    return 0;
}

IDE podaje nst. błąd: error: request for memebr 'wiek' in Karol2, which is of non-class type 'Osoba()'
O co chodzi?
EDIT: chyba znalazłem, musiałem z instrukcji Osoba Karol2(); wyrzucić nawiasy, ponieważ nie podajemy żadnych argumentów. W tym był błąd?

2

Tak. Kompilator odbiera Osoba Karol2() jako deklarację funkcji Karol2, która nie przyjmuje żadnych argumentów i zwraca Osoba.

0
Althorion napisał(a):

Tak. Kompilator odbiera Osoba Karol2() jako deklarację funkcji Karol2, która nie przyjmuje żadnych argumentów i zwraca Osoba.

Mam jeszcze pytanie: jak zainicjować w klasie pole const, jak to powiedziałeś," bezpośrednio (const int wiek {18})", To const int wiek {18} mam wrzucić do nawiasu obok Osoba, czy jak? Bo podczas próby kompilacji to nie działa.
I jeszcze jedno: Kq w przykładzie napisał "Osoba() : wiek{42}, wzrost{190} {}". Dlaczego są tutaj nawiasy klamrowe, zamiast zwykłych okrągłych. Ja dałem okrągłe i też działa.

1

Mam jeszcze pytanie: jak zainicjować w klasie pole const, jak to powiedziałeś," bezpośrednio (const int wiek {18})", To const int wiek {18} mam wrzucić do nawiasu obok Osoba, czy jak?

Nie, bezpośrednio w ramach deklaracji zmiennej. Wówczas nie będzie oczywiście możliwości ustawienia innej wartości, wszystkie instancje będą miały taką samą. Przykładowe zastosowanie: stała fizyczna potrzebna danej klasie.

Dlaczego są tutaj nawiasy klamrowe, zamiast zwykłych okrągłych. Ja dałem okrągłe i też działa.

Nawiasy klamrowe to lista inicjalizacyjna, nawiasy okrągłe to jawne wywołanie konstruktora. Te pierwsze mają tę przewagę, że zawsze oznaczają to samo i nigdy nie zostaną zinterpretowane jako deklaracja funkcji.

0
kario97 napisał(a):

I jeszcze jedno: Kq w przykładzie napisał "Osoba() : wiek{42}, wzrost{190} {}". Dlaczego są tutaj nawiasy klamrowe, zamiast zwykłych okrągłych. Ja dałem okrągłe i też działa.

Jeśli chodzi o inicjalizację to polecam obejrzeć:
CppCon 2018: Nicolai Josuttis “The Nightmare of Initialization in C++”

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