본문 바로가기
C++ STL, 알고리즘

C++ 상속

by wanna_dev 2024. 10. 2.

 

날씨 예보를 위한 WeatherPrediction 클래스

class WeatherPrediction
{
public:
	virtual void setCurrentTempFahrenheit(int inTemp);
	virtual void setPositionOfJupiter(int inDistanceFromMars);
	virtual int getTomorrowTempFahrenheit();
	virtual double getChanceOfRain();
	virtual void showResult();
	virtual std::string getTemperature() const;
protected:
	int mCurrentTempFahrenheit;
	int mDistanceFromMars;
};
#include "WeatherPrediction.h"
class MyWeatherPrediction : public WeatherPrediction
{
public :
	virtual void setCurrentTempCelsius(int inTemp);
	virtual int getTomorrowTempCelsius();
	virtual void showResult();
	
	virtual std::string getTemperature() const; //override 
protected:
	static int convertCelsiusToFahrenheit(int inCelsius);
	static int convertFahrenheitToCelsius(int inFahrenheit);
};

void MyWeahterPrediction::setCurrentTempCelsius(int temp){
	int fahrenheitTemp = convertCelsiusToFahrenheit(inTemp);
	setCurrentTempFahrenheit(fahrenheitTemp);
}

int MyWeahterPrediction::getTomorrowTempCelsius(){
	int fahrenheitTemp = getTomorrowTempFahrenheit();
	return convertFahrenheitToCelsius(fahrenheitTemp);
}
void MyWeatherPrediction::showResult(){
	
}
string MyWeatherPrediction::getTemperature() const{
	//return getTemperature()+" F"; //bug!!!! 이유 : c++에서는 먼저 현재의 스코프,
	//즉 MyWeatherPrediction에서 메서드 이름을 찾기 때문에 getTemperature()의 호출은 
	//MyWeatherPrediction::getTemperature()의 호출과 같다.
	//이 때문에 이 코드는 스택 메모리가 부족해져서 무한 재귀
	return WeatherPrediction::getTemperature()+"F"; //정상동작
	//return __super::getTemperature()+"F"; //정상동작
}

생성순서

  1. 클래스가 부모 클래스를 가졌으면 부모 클래스의 디폴트 생성자가 실행된다.
  2. static이 아닌 클래스 멤버들이 선언 순서에 맞추어 생성된다.
  3. 클래스의 생성자 바디가 실행된다.
class Something{
public :
	Something(){cout<<"2";}
};

class Parent{
public:
	Parent(){cout<<"1";}
};

class Child:public Parent{
public:
	Child(){cout<<"3";}
protected:
	Something mDataMember;
}

int main(){
	Child myChild;
	return 0;
}

// 결과 : 123

소멸자 순서

  1. 클래스의 소멸자 바디 호출
  2. 데이터 멤버를 생성 순서와 반대순으로 삭제
  3. 부모 클래스가 있다면 소멸자를 호출
class Something{
public:
	Something(){cout<<"2";}
	virtual ~Something(){cout<<"2";}
};
class Parent{
public:
	Parent(){cout<<"1";}
	virtual ~Parent(){cout<<"1";}
};
class Child:public Parent{
public:
	Child(){cout<<"3";}
	virtual ~Child(){cout<<"3";}
protected:
	Something mDataMember;
};
int main(){
	Child myChild;
	return 0;
}
// 123321

소멸자는 virtual 로 선언할 것

class Something{
public:
	Something(){cout<<"2";}
	~Something(){cout<<"2";} //동작은 하지만 virtual 로 선언하는 것이 좋음
};
class Parent{
public:
	Parent(){cout<<"1";}
	 ~Parent(){cout<<"1";} //버그! 반드시 virtual 이어야만 한다.
};
class Child:public Parent{
public:
	Child(){cout<<"3";}
	~Child(){cout<<"3";} //동작은 하지만 virtual로 선언되는 것이 좋다.
protected:
	Something mDataMember;
};
int main(){
	Parent* ptr = new Child();
	delete ptr;
	return 0;
}
// 1231

부모 클래스만이라도 소멸자를 명시적 virtual 로 선언해주어야한다.

업캐스팅 시 참조를 이용하면 슬라이싱이 발생하지 않는다.

Super mySuper = mySub; //슬라이싱 발생
Super& mySuper = mySub; //슬라이싱이 발생하지 않음

다운캐스팅을 꼭 써야만 한다면 dynamic_cast를 이용해야한다.

static_cast와 달리 dynamic_cast는 객체에 저장된 정보를 이용하여 해당 캐스팅이 적합한지 런타임에 검사하여 문제가 있으면 캐스팅을 거부하고 에러를 발생시킨다.

캐스팅 실패가 포인터 변수에 대해서 발생하면 결과 값으로 nullptr가 세팅되어 무의미한 객체를 가리키게 된다.

만약 참조형 변수에 대해 캐스팅이 실패했다면 std::bad_cast Exception이 발생한다.

다중상속시 모호한 함수이름 에대한 해결 : 스코프 지정 연산자를 지정한다.

다이아몬드 다중상속 모호성 해결 : 최상위 클래스를 모든 메서드가 퓨어 버추얼인 추상 클래스로 만드는 것

class Animal
{
public : 
	virtual void eat() = 0;
}
class Dog :public Animal
{
public : 
	virtual void bark() {cout <<"Woof!" << endl;}
	virtual void eat() {cout<<"The dog has eaten."<< endl;}	
}
class Bird: 

using keyword쓰임

class Super{
public: 
	Super(const std::string& str);
};

class Sub : public Super{
public: 
	Sub(int i);
}
Super super("Hello");
Sub sub1(1);
Sub sub2("Hello");

메서드 오버라이딩의 특수한 경우

  1. 베이스 클래스가 static인 경우
    1. static과 virtual을 동시에 지정할 수 없다. static 메서드를 오버라이드 하면 원래 의도와 다른 효과가 발생한다. 파생클래스에 있는 static 메서드의 이름이 베이스 클래스의 static 메서드와 같으면 서로 달느 메서드 두개가 생성된다. 1.
    2. class BaseStatic{ public: static void beStatic(){ cout<<"BaseStatic being static" <<endl; } }; class DerivedStatic : public BaseStatic{ public: static void beStatic(){ cout<<"DerivedStatic keepin' it static" } }
  2. 베이스 클래스 메서드가 오버로드 된 경우
    1. 베이스 클래스에 다양한 버전으로 오버로드된 메서드가 여러개 있는데 그중 한 버전만 오버라이드 하면 컴파일러는 베이스 클래스에 있는 다른 버전의 메서드도 함께 가려버린다. 이렇게 하는 이유는 컴파일러 입장에서 볼때 어느 한 버전만 오버라이드 했다는 것은 원래 같은 이름을 가진 모든 메서드를 오버라이드하려다가 깜빡 잊고 하나만 적었다고 판단하기 때문이다.
    class Base{
    public:
    	virtual ~Base()=default;
    	virtual void overload(){ cout << "Base's overload()"<< endl;}
    	virtual void overload(int i){
    		cout<<"Base's overload(int i)" << endl;
    	}
    };
    class Derived : public Base{
    public:
    	virtual void overload() override{
    		cout<< "Derived's overload()"<<endl;
    	}
    }
    
  3. private 나 protected로 선언된 베이스 클래스 메서드
    1. private나 protected 메서드도 얼마든지 오버라이드 할 수 있다. 메서드에 대한 접근 지정자는 그 메서드를 호출할 수 있는 대상만 제한할 뿐이다.
    class MilesEstimator{
    public:
    	virtual ~MilesEstimator()=default;
    	virtual int getMilesLeft() const;
    	virtual void setGallonsLeft(int gallons);
    	virtual int getGallonsLeft() const;
    private:
    	int mGallonsLeft;
    	virtual int getMilesPerGallon() const;
    }
    int MilesEstimator::getMilesLeft() const{
    	return getMilesPerGallon()*getGallonsLeft();
    }
    void MilesEstimator::setGallonsLeft(int gallons){
    	mGallonsLeft = gallons;
    }
    int MilesEstimator::getGallonsLeft() const{
    	return mGallonsLeft;
    }
    int MilesEstimator::getMilesPerGallon() cosnt{
    	return 20;
    }
    class EfficientCarMilesEstimator: public MilesEstimator{
    private:
    	virtual int getMilesPerGallon() const override;
    }
    int EfficientCarMilesEstimator::getMilesPerGallon() const{
    	return 35;
    }
    int main(){
    	myMilesEstimator.setGallonsLeft(2);
    	cout<<"Normal estimator can go"<< myMilesEstimator.getMilesLeft() <<"more miles"<<endl;
    	return 0;
    }
    
  4. 베이스 클래스 메서드에 디폴트 인수가 지정된 경우
    1. 파생 클래스와 베이스 클래스에서 지정한 디폴트 인수가 서로 다를수 있다. 그런데 실행할때 적용할 인수는 실제 내부에 있는 객체가 아닌 변수에 선언된 타입에 따라 결정된다.
  5. 베이스 클래스 메서드와 접근 범위를 다르게 지정하는 경우

출처 : 전문가를 위한 C++

'C++ STL, 알고리즘' 카테고리의 다른 글

스마트 포인터 c++  (0) 2024.10.07
memory leak detection  (0) 2024.10.02
const  (0) 2024.10.02
C++ 생성자  (0) 2024.10.02
OOP  (0) 2024.10.02