» »

[c++]Problem z razredi

[c++]Problem z razredi

killa bee ::

Zanima me zakaj na outputu ne prikaže "James 1200"?
main
	
int main(){
PEmployee one;
	PEmployee two;

	PEmployee three("James", 1000);
	three.set_salary(1200);
	
	cout << three.get_name() << "  " << three.get_salary() << '\n';

	cout << "Press any key to countinue...";
	cin.get();
return 0;}



Person.h
#ifndef Person_H
#define Person_H

#include <iostream>
#include <string>
using namespace std;


class Person{

public:
	Person();
	Person(string pname, int page);
	string get_name() const;
	void get_age() const;
private:
	string name;
	int age; // 0 if unknown

};

#endif 

PEmployee.h
#ifndef PEmployee_H
#define PEmployee_H

#include "Person.h"
#include <iostream>
#include <string>
using std::string;

class PEmployee
{
public:
	PEmployee();
	PEmployee(string employee_name, double initial_salary);
	void set_salary(double new_salary);
	double get_salary() const;
	string get_name() const;
private:
	Person person_data;
	double salary;
};

#endif

Person.cpp
#include "Person.h"

using namespace std;


Person::Person(){

	name = "noname";
	age = 0;

}

Person::Person(string pname, int page){

	name = pname;
	age = page;

}

string Person::get_name() const{

	return name;

}

void Person::get_age() const{

	cout << age << '\n';

}

PEmployee.cpp
#include "PEmployee.h"

using namespace std;

PEmployee::PEmployee(){
	
	Person person_data();
	salary = 0;
	
}

PEmployee::PEmployee(string employee_name, double initial_salary){

	Person person_data(employee_name, 0);
	salary = initial_salary;

}



void PEmployee::set_salary(double new_salary){
	
	salary = new_salary;
}


double PEmployee::get_salary() const{
	
	return salary;

}

string PEmployee::get_name() const{

	return person_data.get_name();  
}

output
noname 1200

Vesoljc ::

ker je person data samo zacasni objekt, ki se unici ko se empolyer konstruktor konca.
Abnormal behavior of abnormal brain makes me normal...

Vesoljc ::

bolj verjetno je da hoces uporabiti dedovanje:
class Employer : public Person
{
   // poklices se base class kontruktor
   public Employer(string name, int age, int es) : Person(name,age) 
   {
       salary = es;
   }
}

Employer e1("vesoljc", 33, 5000);  // notice the funny part ;)
cout << e1.getname() << e1.getsalary();
Abnormal behavior of abnormal brain makes me normal...

killa bee ::

to se mi pa res ne zdi logično. String in int spremenljivke se ko se konstruktor izvede ostanejo za stalno. Če je pa tipa Person pa ne ostane za stalno.
SEpravi se ko se konstruktor
PEmployee three("James", 1000);
izvede se v bistvu izvedeta 2 konstruktorja. Person() ter Person(James,0)?

mn ::

killa bee je izjavil:

to se mi pa res ne zdi logično. String in int spremenljivke se ko se konstruktor izvede ostanejo za stalno. Če je pa tipa Person pa ne ostane za stalno.
SEpravi se ko se konstruktor
PEmployee three("James", 1000);
izvede se v bistvu izvedeta 2 konstruktorja. Person() ter Person(James,0)?


Narobe. Mešaš lokalne variable (se jih uniči ko se konča funkcija) in member variable (se jih uniči ko se uniči parent objekt). Mankajo ti osnove zato si poglej recimo si "variable scope and lifetime in c++"

Recimo tole :

killa bee ::

PEmployee three("James", 1000)
ko se izvede da vrstica se v bistvu izvedeta 2 konstruktoraja. Person() ter Person("James",1000). Prvi postavi vrednost spremenljivke name na "noname", drugi se pa izvede ampak se verjetno ne shrani??

Senitel ::

Saj ti je bilo razloženo... Kaj naredi tale vrstica?
Person person_data(employee_name, 0);

killa bee ::

Postavi vrednosti spremenljivke instance(person_data) razreda Person -name na "James" in -age na 0.

Zgodovina sprememb…

  • spremenilo: killa bee ()

Vesoljc ::

ja nastavi, ampak se enkrat, ta instanca je zacasna in se unici ko se employer konstruktor konca. ta instanca ni tista katero ti mislis da spreminjas. ti naredis novo lokalno instanco, ki ima "slucajno" isto ime kot member instanca.
odlocit se moras kako hoces imeti organizirane objekte.
http://stackoverflow.com/questions/9677...
Abnormal behavior of abnormal brain makes me normal...

killa bee ::

ja nastavi, ampak se enkrat, ta instanca je zacasna in se unici ko se employer konstruktor konca

Zakaj bi se pa uničila?
Kdo pa potem nastavi spremenljivko -name na "noname"?

mn ::

killa bee je izjavil:

ja nastavi, ampak se enkrat, ta instanca je zacasna in se unici ko se employer konstruktor konca

Zakaj bi se pa uničila?
Kdo pa potem nastavi spremenljivko -name na "noname"?


Si prebral kaj o življenski dobi spremenljivk na netu? Tukaj sprašuješ povsem osnovne stvari ki si bi jih moral razčistiti preden napišeš prvo vrstico kode.

"person_data" spremenljivka se uniči tisti trenutek ko gre "out of scope". Funkcija se zaključi, stack se unwinda, in vsem objektom, ki so na stacku se najprej kliče destruktor potem pa se pomnilnik sprosti.

Pa daj res, preberi si malo o teh stvareh.

smoke ::

killa bee je izjavil:


PEmployee.cpp
#include "PEmployee.h"

using namespace std;

PEmployee::PEmployee(){

Person person_data();
salary = 0;

}


Person person_data(); NI lokalna spremenljivka, temveč deklaracija funkcije z imenom person_data, ki ne sprejme nobenega parametra in vrača objekt Person.

mn ::

smoke je izjavil:

killa bee je izjavil:


PEmployee.cpp
#include "PEmployee.h"

using namespace std;

PEmployee::PEmployee(){

Person person_data();
salary = 0;

}


Person person_data(); NI lokalna spremenljivka, temveč deklaracija funkcije z imenom person_data, ki ne sprejme nobenega parametra in vrača objekt Person.


Seveda je lokalna spremenljivka.

smoke ::

@mn, očitno ne poznaš C++. Sicer ti pa vsak malo boljši prevajalnik javi opozorilo. Primer:

class A {};

int main(int argc, char** argv) {
  A a();
}


Ko prevedeš tako kodo s Clang-om 3.5 dobiš tole:
main.cpp:4:6: warning: empty parentheses interpreted as a function declaration [-Wvexing-parse]
  A a();
     ^~
main.cpp:4:6: note: remove parentheses to declare a variable
  A a();
     ^~
1 warning generated.

mn ::

@smoke Sem preveril in drži. Zanimivo, tega res nisem vedel (ali pa že pozabil).

This is known as "C++'s most vexing parse". Basically, anything that can be interpreted by compiler as a declaration will be interpreted as a declaration.

killa bee ::

Jst nikoli ne kličem f-je "Person person_data();" ampak "Person person_data("James", 1000);" ki je drug konstruktor kot samo Person person_data()

Vesoljc ::

mn je izjavil:

@smoke Sem preveril in drži. Zanimivo, tega res nisem vedel (ali pa že pozabil).

This is known as "C++'s most vexing parse". Basically, anything that can be interpreted by compiler as a declaration will be interpreted as a declaration.


that makes two of us... ;)
Abnormal behavior of abnormal brain makes me normal...

smoke ::

killa bee je izjavil:

Jst nikoli ne kličem f-je "Person person_data();" ampak "Person person_data("James", 1000);" ki je drug konstruktor kot samo Person person_data()


Kot ti je @Vesoljc že povedal imaš tukaj problem (v tretji vrstici):
PEmployee::PEmployee(string employee_name, double initial_salary){
 
    Person person_data(employee_name, 0);
    salary = initial_salary;
 
}


LOKALNA spremenljivka person_data obstaja samo za čas izvajanja konstruktorja. Ko pa pokličeš get_name() pa dostopaš do razredne spremenljivke, ki je bila nastavljena ko se je poklical privzeti konstruktor RAZREDNE spremenljivke person_data.

Rešitev bi bila recimo to:

PEmployee::PEmployee(string employee_name, double initial_salary)
    : person_data(employee_name, 0), salary(initial_salary) {}


Privzeti konstruktor pa naj bo kar to:
PEmployee::PEmployee() {}

Imaš tudi konceptualno narobe zastavljeno. In sicer kompozicija implicira "has-a" povezavo med dvema razredoma, ki pa ne ustreza tvojemu primeru (ni res da Delojemalec IMA Osebo) med tem ko pa dedovanje implicira povezavo "is-a" med podrazredom in nadrazredom kar natanko ustreza tvojemu primeru (Delojemalec JE Oseba). Tukaj imaš primer kako bi jaz zadevo rešil:

#include <iostream>
#include <string>

class Person {
 public:
  Person(const std::string& name, unsigned int age) : name_(name), age_(age) {}

  std::string GetName() const { return name_; }
  void SetName(const std::string& name) { name_ = name; }

  unsigned int GetAge() const { return age_; }
  void SetAge(unsigned int age) { age_ = age; }

  friend std::ostream& operator<<(std::ostream& ostream, const Person& person);

 private:
  std::string name_;
  unsigned int age_;
};

class Employee : public Person {
 public:
  Employee(const std::string& name, unsigned int age, float salary)
      : Person(name, age), salary_(salary) {}

  float GetSalary() const { return salary_; }
  void SetSalary(float salary) { salary_ = salary; }

  friend std::ostream& operator<<(std::ostream& ostream,
                                  const Employee& employee);

 private:
  float salary_;
};

std::ostream& operator<<(std::ostream& ostream, const Person& person) {
  ostream << person.name_ << " " << person.age_;
  return ostream;
}

std::ostream& operator<<(std::ostream& ostream, const Employee& employee) {
  ostream << employee.GetName() << " " << employee.GetAge() << " "
          << employee.salary_;
  return ostream;
}

int main() {
  Employee employee("Janez Novak", 45, 32000);
  employee.SetSalary(40000);

  std::cout << employee << std::endl;
  return 0;
}

Zgodovina sprememb…

  • spremenil: smoke ()

killa bee ::

Ko pa pokličeš get_name() pa dostopaš do razredne spremenljivke, ki je bila nastavljena ko se je poklical privzeti konstruktor RAZREDNE spremenljivke person_data.
Kdaj se je pa privzeti konstruktor poklical za objekt "three" ? Jst ga nisem poklical.

drugače pa so to navodila od naloge
Exercise P5.1. Implement all member functions of the following class:
class Person
{
public:
Person();
Person(string pname, int page);
void get_name() const;
void get_age() const;
private:
string name;
int age; // 0 if unknown
};
------------------------------------------------------------------------------------------
Exercise P5.2. Implement a class PEmployee that is just like the Employee class except
that it stores an object of type Person as developed in Exercise P5.1.
class PEmployee
{
public:
PEmployee();
PEmployee(string employee_name, double initial_salary);
void set_salary(double new_salary);
double get_salary() const;
string get_name() const;
private:
Person person_data;

Zgodovina sprememb…

  • spremenilo: killa bee ()

smoke ::

killa bee je izjavil:

Ko pa pokličeš get_name() pa dostopaš do razredne spremenljivke, ki je bila nastavljena ko se je poklical privzeti konstruktor RAZREDNE spremenljivke person_data.
Kdaj se je pa privzeti konstruktor poklical za objekt "three" ? Jst ga nisem poklical.


Seveda si ga poklical, implicitno, ko si ustvaril objekt PEmployee. Ko v C++ ustvariš objekt, se, preden se izvede konstruktor tega objekta rekurzivno inicialicirajo vse spremenljivke ki so člani tega razreda. Recimo privzeta inicializacija v primeru celoštevilskih tipov pomeni inicializacija na vrednost 0 (zero initialization), v primeru razredov pa pomeni da se pokliče privzeti konstruktor (to je samo načeloma, lahko da privzetega konstruktorja ni, je pa recimo konstruktor, ki sprejema en parameter in ima ta parameter privzeto vrednost). V primeru, da pa privzetega konstruktorja (ali takšnega, ki bi lahko prevzel vlogo privzetega konstrutorja) ni, pa nam C++ omogoča, da v inicializacijski listi (member initializer list) določimo kateri konstruktor naj se pokliče za nekega člana.

Sem bil dovolj razumljiv? :)

killa bee ::

Sepravi vedno se izvede konstruktor, ki ima initializer list. Privzeti se pa izvede samo če ni initializer lista ?

smoke ::

Ne. Poglej ta primer:

class A {
 public:
  A() {}
  A(int x) : x_(x) {}

 private:
  int x_;
};

class B {
 public:
  B() {}
  B(int x) : a_(x) {}

 private:
  A a_;
};

int main() {
  B b1;
  B b2(5);
}


Vidimo da ima razred B v sebi spremenljivko tipa A. Če razred B instanciramo brez parametrov (primer je spremenljivka b1 v mainu), se bo, tik preden se bo začelo izvajati telo konstruktorja razreda B, izvedel privzeti konstruktor spremenljivke a_ in jo inicializiral. V primeru b2 se pa privzeti konstruktor a_ ne bo poklical ampak se bo poklical konstruktor, ki sprejema en parameter tipa int. Torej lahko vidiš, da čeprav nisi nič posebnega naredil, se je v primeru b1, poklical privzeti konstruktor spremenljivke a_, tako kot v tvojem primeru ko si naredil PEmployee, ki je imel spremenljivko person_data v sebi.

Je zdaj kaj bolj jasno?

killa bee ::

ampak se bo poklical konstruktor, ki sprejema en parameter tipa int.
ta je definiran v 4 vrstici ?

Je možno definirat razred brez privzetega konstruktorja?
primer:
class A {
 public:
  A(int x) : x_(x) {}
 
 private:
  int x_;
};

Zgodovina sprememb…

  • spremenilo: killa bee ()

Senitel ::

Lahko ne napišeš privzetega konstruktorja, ampak ga bo compiler še vedno avtomatsko generiral. Lahko ga pa prepoveš v stilu:
class A {
private:
  A() {}

  int x_;
public:
  A(int x) : x_(x) {}
};

Ampak to nima direktne veze s tvojim problemom, da ne razumeš kako sploh deluje scope in kako se instancirajo objekti.

smoke ::

Senitel je izjavil:

Lahko ne napišeš privzetega konstruktorja, ampak ga bo compiler še vedno avtomatsko generiral. Lahko ga pa prepoveš v stilu:

class A {
private:
A() {}

int x_;
public:
A(int x) : x_(x) {}
};

Ampak to nima direktne veze s tvojim problemom, da ne razumeš kako sploh deluje scope in kako se instancirajo objekti.


Vbistvu to ne drži čisto. Če definiraš katerikoli konstruktor (lahko je kopirni), potem prevajalnik ne naredi privzetega. Recimo:

class A {
 public:
  A(const A&) {}
};


Zgornji razred A lahko teoretično instanciramo samo kot kopijo druge instance razreda A.

@killa bee: Se pa pridružujem Senitelovemu komentarju, očitno ti stvari še niso povsem jasne. Vzemi v roke kako knjigo o osnovah C++.

killa bee ::

Je zdaj kaj bolj jasno?
bolj jasno je.

sepravi v tej vrstici se v prvi vrstici kode programa :
B b1;

se najprej izvede
A() {}

potem pa šele
B() {}

********************************************************************************
v naslednj vrstici ko se izvede
B b2(5);

se bo torej izvedel samo ta konstruktor
B(int x) : a_(x) {}

in potem še ta:
A(int x) : x_(x) {}

mn ::

Še en nasvet.

V te konstruktorje dodaj "debug" izpise in boš lahko sam ugotovil kateri konstruktor se ti prej izvede in kateri kasneje. Recimo na zažetek vsake funkcije dodaj:

std::cout << "Ime Funkcije" << std::endl;


Vredno ogleda ...

TemaSporočilaOglediZadnje sporočilo
TemaSporočilaOglediZadnje sporočilo
»

Taboo - TV serija

Oddelek: Sedem umetnosti
468164 (403) oo7
»

TOP filmi 2017

Oddelek: Sedem umetnosti
409162 (6761) bambam20

COD 2 ne deluje

Oddelek: Igre
142835 (1844) Heavy
»

Izgubil sem ključe od avta!

Oddelek: Loža
366551 (5307) IgorGrozni
»

Pirates of the Caribbean LoJS

Oddelek: Igre
151454 (1032) begy

Več podobnih tem