• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Dziedziczenie i preambuła konstruktora.

Object Storage Arubacloud
0 głosów
919 wizyt
pytanie zadane 3 lipca 2016 w C i C++ przez niezalogowany

Dzień dobry, uczę się obiektówki z kursów Pana Zelenta i zaciąłem się przy odc. 5 - dziedziczeniu.
Do rzeczy - Pan Zelent stworzył klasę Kolo, dziedziczącą z klasy Punkt. Następnie stworzył konstruktor:

 Kolo(string nk="Kolko", string np="S", float a=0, float b=0, float pr=1)
:Punkt(np,a,b)
{
nazwa = nk;
r = pr;
}

I nie za bardzo rozumiem, skoro samemu konstruktorowi przypisuje się już wartości domyślne, to po co jeszcze podczepiać pod to konstruktor Punkt(np,a,b)? Z tego co sprawdziłem, to to podczepienie nic nie daje, bo tak czy siak MUSZĘ zadać jakieś wartości domyślne dla Kolo, inaczej kompilator krzyczy, że nie ma poprawnego konstruktora. (a wartości z Punkt(np,a,b) jakby wcale nie brały żadnego udziału)

I mam jeszcze jeden problem - chciałem dodać możliwość wpisywania własnych wartości - dla punktu wszystko działa - ale dla koło zapisuje tylko r i swoją nazwę, natomiast nie wczytuje wcześniej utworzonego punktu(czyta wartości domyślne) - i co z tym zrobić?
Podrzucam swój kod, może ktoś z Was mi pomoże to ogarnąć..

obiekty.h
 

#include <iostream>

using namespace std;

class Punkt 
{
	float x, y;
	string name;

public:
	Punkt(string = "A", float a = 0, float b = 0);
	//void wczytaj();
	void wyswietl();
	~Punkt();
};
class Kolo :Punkt 
{
	float r;
	string name;
public:
	Kolo(string np, float a, float b, float rr = 1, string nk = "Kolko");
	//void wczytaj();
	void wyswietl();
	~Kolo();
};

obiekty.cpp
 

#define _USE_MATH_DEFINES
#include <iostream>
#include <string>
#include "obiekty.h"
#include <cmath>

using namespace std;

Punkt::Punkt(string n, float a, float b) {
	name = n;
	x = a;
	y = b;
}

Punkt::~Punkt()
{
}

void Punkt::wyswietl(){
	cout << endl << "Punkt " << name << "(" << x << "," << y << ")";
}
//void Punkt::wczytaj() {
//	cout << endl << "Podaj x: ";
//	cin >> x;
//	cout << endl << "Podaj y: ";
//	cin >> y;
//	cout << endl << "Podaj nazwe: ";
//	cin >> name;
//}

Kolo::Kolo(string np, float a, float b, float rr, string nk)
	:Punkt(np, a, b) {
	name = nk;
	r = rr;
}

void Kolo::wyswietl() {
	cout << endl << "Kolo " << name << " o promieniu " << r << " w punkcie ";
	Punkt::wyswietl();
	cout << endl << "ma pole rowne: " << M_PI*r*r;
}
Kolo::~Kolo()
{
}
//void Kolo::wczytaj() {
//	cout << endl << "Podaj r: ";
//	cin >> r;
//	cout << endl << "Podaj nazwe: ";
//	cin >> name;
//}

main.cpp

#include <iostream>
#include <cstdio>
#include "obiekty.h"

using namespace std;

int main(void) {
	Punkt p1;
	//p1.wczytaj();
	p1.wyswietl();
	Kolo k1;
	//k1.wczytaj();
	k1.wyswietl();
	getchar();
	return 0;
}

Będę bardzo wdzięczny za kilka słów wyjaśnienia i uwagi dot. moich kodów.
Pozdrawiam.

2 odpowiedzi

0 głosów
odpowiedź 3 lipca 2016 przez Michał Muzyka Pasjonat (24,080 p.)
wybrane 3 lipca 2016
 
Najlepsza

Błędem jest to że koło nie ma domyślnego konstruktora i nie możesz tworzyć go za pomocą:

Kolo k1;

tylko musi być:

Kolo k1("A", 1, 1 , 3, "Kolo");  //tworzy kolo o srodku w punkcie A (1,1) i promieniu 3 i nazwie "Kolo"



Podczepienie tego konstruktora ma na celu zainicjowanie zmiennych które są dziedziczone z klasy Punkt, można zrobić to tak:
 

Kolo(string nk="Kolko", string np="S", float a=0, float b=0, float pr=1)
   :Punkt(np, a, b)
{
nazwa = nk;
r = pr;
}

ale powinno się to robić tak:

Kolo::Kolo(string np, float a, float b, float rr, string nk)
    :Punkt(np, a, b), name(nk), r(rr)
 {
}

bo szybsze dla programu jest inicjowanie przy użyciu listy inicjalizacyjnej (ta część po :) oraz gdy dziedziczysz obiekty tworzone są jak "cebula", dodawane są kolejne warstwy, czyli musisz uruchamiać konstruktory poprzednich warstw oraz  gdy mniej jest wartości w ciele konstruktora kompilator zastosuje wobec niego większą optymalizację

Wczytywanie nie działa bo ładowałeś wartości najpierw do obiektu punkt p1 a potem do obiektu koło k1, które nie mają za sobą nic wspólnego. Powinno być tak:
 

	void Kolo::wczytaj() {
	  Punkt::wczytaj();
	  cout << endl << "Podaj r: ";
	  cin >> r;
	  cout << endl << "Podaj nazwe: ";
	  cin >> name;
	}

 

komentarz 3 lipca 2016 przez niezalogowany

Błędem jest to że koło nie ma domyślnego konstruktora

To już nie rozumiem, konstruktor jest specjalną metodą, która zostaje uruchomiona wraz z utworzeniem danego obiektu i która m.in. ustawia wartości początkowe, ale ja taką metodę mam przecież zapisaną.. 

w obiekty.h deklaracja

class Kolo :Punkt 
{
	float r;
	string name;
public:
	Kolo(string np, float a, float b, float rr, string nk);
	void wczytaj();
	void wyswietl();
	~Kolo();
};

w obiekty.cpp przypisanie wartości(definicja)

Kolo::Kolo(string np, float a = 0, float b = 0, float pr = 1, string nk = "Kolko")
    :Punkt(np, a, b), name(nk), r(pr) {
    name = nk;
    r = pr;
}

chyba, że to nie jest konstruktor - ale w takim przypadku - co?
Zgodnie z Twoją wypowiedzią i Szykem2 - wpierw w pamięci rezerwowane jest miejsce na klasę bazową Punkt(jej zmienne), następnie rezerwowane jest miejsce na klasę pochodną, tj. Kolo(jej zmienne), następnie tworzy się obiekt Kolo k1, który uruchamia konstruktor Kolo(który z jakiegoś powodu nie istnieje?), który ustawia część swoich danych, a część z uruchomionego w następstwie konstruktora Punkt z listy inicjalizacyjnej. Dobrze?

A co do wczytywania - jeśli dobrze zrozumiałem, to uruchamiam funkcję wczytaj z klasy Kolo, która ma wczytać funkcję wczytaj z klasy Punkt, która zapisuje x,y,name dla jakiegoś tymczasowego obiektu klasy Punkt, który przekazuje następnie owe x,y,name do obiektu klasy Kolo - dobrze to rozumiem?

komentarz 3 lipca 2016 przez Michał Muzyka Pasjonat (24,080 p.)

jak wywołujesz metodę Punkt::wczytaj() to ona ładuje zmienne które klasa dziedziczy z klasy punkt.

Tak, konstruktor ładuje się przy tworzeniu obiektu ale konstruktorów jest kilka rodzajów i możesz mieć kilka konstruktorów w klasie
np. konstruktor domyślny któremu musisz podać wszystkie dane jakich ma używać:

plik .h
Kolo(string np="S", float a=1, float b=1, float rr=3, string nk="Kolo");//konstruktor domyślny

możesz dzięki temu możesz zrobić to:

Kolo k1;

ale możesz zrobić konstruktor który ma nie uzupełniać za ciebie danych:

Kolo(string np, float a, float b, float rr, string nk);//konstruktor

a jak masz to to musisz zrobić to:

Kolo k1("S", 1,1,3, "Kolo");

są jeszcze konstruktory kopiujące i konwertujące ale je ominiemy

komentarz 3 lipca 2016 przez niezalogowany

Aaa, ok, to trochę mi rozjaśniło, dziękuję. Ale nadal nie rozumiem listy inicjalizacyjnej. Znowu - jak to rozumieć?

Kolo::Kolo(string np, float a, float b, float rr, string nk)
    :Punkt(np, a, b), name(nk), r(rr)
 {
}

Lista inicjalizacyjna powoduje, że "name" dziedziczy wartość z "nk" i "r" z "rr"? Nie za bardzo rozumiem ten zapis.

komentarz 3 lipca 2016 przez Michał Muzyka Pasjonat (24,080 p.)

nie, lista inicjalizacyjna jest po to aby nadać wartości zmiennym oraz aby wywołać konstruktory inne niż domyślne.

:name(nk), r(rr)

po prostu nadaje wartości zmiennym ale prawdopodobnie zrobi to szybciej niż przypisanie w konstruktorze takie jak name = nk;

:Punkt(np, a, b)

po prostu wywołuje konstruktor inny niż domyślny bo inaczej nie można wywołać konstruktora dla naszych wartości
 

 

komentarz 3 lipca 2016 przez niezalogowany
Dobra, teraz już wszystko rozumiem. Bardzo dziękuję za pomoc!
0 głosów
odpowiedź 3 lipca 2016 przez Szykem2 Nałogowiec (29,510 p.)

Twój problem polega na braku zrozumienia jak w C++ obiekt jest budowany. Na początku jest rezerwowana pamięć na cały obiekt. Później niejako tworzone są obiekty nadrzędne(bazowe) w odpowiedniej kolejności(z góry na dół z lewa na prawo, dokładnie tak mówi standard chodzi o kolejność dziedziczenia ale to nieistotne). Na końcu jest tworzony obiekt, który chcesz utworzyć. Czyli, żeby utworzyć ten konkretny obiekt wywoływane są konstruktory wszystkich klas bazowych. Jeżeli chcesz je wywołać z konkretnymi argumentami musisz ten konkretny konstruktor umieścić na liście inicjalizacyjnej. Jeżeli tego nie zrobisz to kompilator próbuje wywołać konstruktor klasy bazowej z takimi argumentami jakie podane przy tworzeniu obiektu.

Wywoływanie kostruktora Punkt(np, a, b) przypisuje wartości polom i w ciele konstruktora Kolo(...) tylko dwa pola zapisujesz więc nie wiem o co chodzi jak mówisz, że nic się nie dzieje przez wywołanie konstruktora Punkt.

Drugi problem wynika z tego samego. Przy wczytywaniu wartości dla kola musisz wczytać też wartości konieczne dla punktu.

Podobne pytania

0 głosów
1 odpowiedź 203 wizyt
0 głosów
2 odpowiedzi 1,088 wizyt
0 głosów
1 odpowiedź 356 wizyt
pytanie zadane 22 grudnia 2019 w C i C++ przez Jacuchna0 Użytkownik (640 p.)

92,551 zapytań

141,393 odpowiedzi

319,523 komentarzy

61,936 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...