'inheritance'에 해당되는 글 3건

  1. 2013.05.01 class
  2. 2012.08.22 가상 상속 (Virtual Inheritance)
  3. 2012.06.06 상속(inheritance)

class

Programming/Python 2013. 5. 1. 20:41 |


파이썬의 클래스는 새로운 이름 공간을 지원하는 단위이다. 이 이름 공간에는 함수와 변수가 포함될 수 있다. 

모듈과 차이가 있다면, 모듈 파일 단위로 이름 공간을 구성하는 반면, 

클래스는 클래스 영역과 인스턴스 영역 내에 이름 공간을 구성한다. 상속 관계에서 상위클래스는 하위 클래스의 이름 공간에 포함된다.


클래스와 이름 공간

클래스 인스턴스도 이름 공간을 생성한다. 이것은 모듈에 없는 중요한 기능이다. 

클래스는 하나 이상의 인스턴스 객체를 생성하는 틀과 같다.


상속

모듈과의 중요한 차이점은, 클래스는 상속(Inheritance)이 된다는 것이다.

클래스는 다른 클래스와 공통적인 특징을 공유할 수 있으므로, 개별적인 특징만을 새로 정의하는 새로운 클래스를 만들 수 있다. 

즉, 재사용성이 있다.


연산자 중복

파이썬 클래스는 연산자 중복을 지원한다. 각종 산술, 논리, 슬라이싱, 인덱싱 등등의 연산을 중복할 수 있다.

연산자 중복이란, 어떤 연산자를 클래스 객체에서도 사용할 수 있게, 거기에 맞는 함수를 정의하는 것이다.



파이썬에서 클래스에 관련된 꼭 필요한 용어

 class  class 문으로 정의되며, 멤버와 메쏘드를 가지는 객체이다.
 class object

 클래스와 같은 의미로 사용. 클래스를 종종 특정한 대상을 가리키지 않고 일반적을 언급하기 위해서 사용하는 반면에, 클래스 객체는 어떤 클래스를 구체적으로 지정하기 위해서 사용하기도 한다. 

 class instance 

 클래스를 호출하여 만들어지는 객체 

 class instance object  

 클래스 인스턴스와 같은 의미이다. 인스턴스 객체라고 부르기도 한다. 

 member 

 클래스가 갖는 변수 

 method 

 클래스 내의 정의된 함수 

 attribute 

 멤버와 메쏘드 전체를 가리킨다. 즉, 이름 공간의 이름 전체를 의미한다. 

 super class 

 base class 라고 하기도 한다. 어떤 클래스의 상위에 있으며 각종 속성을 서브 클래스로 상속해 준다. 

 sub class 

 derived class 라고 하기도 한다. 슈퍼 클래스로부터 상속된 하위의 클래스를 말한다. 슈퍼 클래스로부터 각종 속성을 상속받으므로 코드와 변수를 공유한다. 



객체지향 프로그래밍에서 클래스들이 가지는 중요한 특성

 inheritance

 기존의 클래스의 속성과 행동을 그대로 받아들이고 추가적으로 필요한 기능을 새로운 클래스에 덧붙이는 것이다. 소프트웨어의 재사용 관점에서 상속은 대단히 중요한 역할을 하며, 프로그램의 개발 시간을 단축시켜 준다. 

 다른 프로그래밍 기법과 객체지향 프로그래밍을 구분하는 중요한 특징이다. A클래스를 슈퍼 클래스로 하는 클래스 B를 생성하였다면 "B is-a A" 관계라고 한다. 

 multiple inheritance 

 두 개 이상의 슈퍼 클래스로부터 상속받는 것. 

 polymorphism 

 상속 관계 내의 다른 클래스의 인스턴스들이 같은 멤버 함수 호출에 대해 각각 다르게 반응하도록 하는 기능. 

 encapsulation 

 메쏘드와 멤버를 클래스 내에 포함시키고 외부에서 접근 가능하도록 인터페이스만을 공개하고 다른 속성들을 내부에 숨기는 것. 

 composition 

 x라는 객체(혹은 다수의 객체)가 클래스 A안에 포함되어 A의 각종 메쏘드를 구현하는데 사용된다. 

 즉, 어떤 객체가 다른 객체에 포함되어 활용되는 것을 말한다. 이때 A가 x를 포함하므로 "has-a" 관계라고 한다. 



'Programming > Python' 카테고리의 다른 글

정적 메소드, 클래스 메소드, 장식자  (0) 2013.05.01
메쏘드의 정의와 호출  (0) 2013.05.01
문자열 이름으로 속성 값 참조  (0) 2013.05.01
모듈의 실행 ( __name__ == '__main__' )  (0) 2013.05.01
Module  (0) 2013.05.01
Posted by scii
:

※ 다중 상속을 하였을 때 함수 호출관계의 모호함은 다른 상황에서도 발생할 수 있다.


#include <iostream>

using namespace std;


class Base

{

public:

Base()

cout<<"Base Constructor"<<endl; 

}


void SimpleFunc()

cout<<"BaseOne"<<endl;

}

};


class MiddleDerived_1 :public  Base

{

public:

MiddleDerived_1()

:Base()

{

cout<<"MiddleDerived_1 Constructor"<<endl;

}


void MiddleFunc_1()

{

SimpleFunc();

cout<<"Middlederived_1"<<endl; 

}

};


class MiddleDerived_2 :public  Base

{

public:

MiddleDerived_2()

:Base()

{

cout<<"MiddleDerived_2 Constructor"<<endl;

}


void MiddleFunc_2()

{

SimpleFunc();

cout<<"Middlederived_2"<<endl; 

}

};


class LastDerived :public MiddleDerived_1, public MiddleDerived_2

{

public:

LastDerived()

:MiddleDerived_1(), MiddleDerived_2()

{

cout<<"LastDerived Constructor"<<endl;

}


void ComplexFunc()

{

MiddleFunc_1();

MiddleFunc_2();

MiddleDerived_2::SimpleFunc();    //이렇게 명시해주지않으면 에러발생.

}                                                       //왜냐면 어느클래스에서 쓸 것인지 모르기 때문.

};                                                              //현재 Base Class는 두개이다.


int main(void)

{

cout<<"객체생성 전 ... "<<endl;

LastDerived ldr;


cout<<"객체생성 후 ... "<<endl;

ldr.ComplexFunc();


return 0;

}



위 예제는 LastDerived 클래스가 Base 클래스를 간접적으로 두 번 상속하였다. 다중상속의 모호한 상황이다!!


이렇듯 하나의 객체 내에 두 개의 Base 클래스 멤버가 존재하기 때문에 complexFunc 함수 내에서 이름만 가지고 SimpleFunc 함수를 호출할 수는 없다.

이 경우에는 다음과 같이, 어느 클래스를 통해서 간접 상속한 Base 클래스의 멤버함수를 호출할 것인지 명시해야 한다. 


ex) MiddleDerived_1::SimpleFunc();

// MiddleDerived_1 클래스가 상속한 Base 클래스의 SimpleFunc 함수호출을 명령!


그런데 이러한 상황에서, Base 클래스의 멤버가 LastDerived 객체에 하나씩만 존재하는 것이 타당한 경우가 대부분이다. 

즉, Base 클래스를 딱 한번만 상속하게끔 하는 것이 더 현실적인 해결책이 될 수 있다.

그리고 이를 위한 문법이 바로 "가상 상속" 이다.


ex) class MiddleDerived_1 :virtual public Base

{    };


ex) class MiddleDerived_2 :virtual public Base

{    };






#include <iostream>

using namespace std;


class Base

{

public:

Base()

cout<<"Base Constructor"<<endl; 

}


void SimpleFunc()

cout<<"BaseOne"<<endl;

}

};


class MiddleDerived_1 :virtual public Base

{

public:

MiddleDerived_1()

:Base()

{

cout<<"MiddleDerived_1 Constructor"<<endl;

}


void MiddleFunc_1()

{

SimpleFunc();

cout<<"Middlederived_1"<<endl; 

}

};


class MiddleDerived_2 : virtual public  Base

{

public:

MiddleDerived_2()

:Base()

{

cout<<"MiddleDerived_2 Constructor"<<endl;

}


void MiddleFunc_2()

{

SimpleFunc();

cout<<"Middlederived_2"<<endl; 

}

};


class LastDerived :public MiddleDerived_1, public MiddleDerived_2

{

public:

LastDerived()

:MiddleDerived_1(), MiddleDerived_2()

{

cout<<"LastDerived Constructor"<<endl;

}


void ComplexFunc()

{

MiddleFunc_1();

MiddleFunc_2();

SimpleFunc();        //virtual inheritance(가상 상속) 을 해주었기때문에

}                                     //Base Class는 하나로 오버라이딩되어 명시해주지 않아도 된다.

};                                            //왜냐면, Base Class가 하나밖에 없기 때문.


int main(void)

{

cout<<"객체생성 전 ... "<<endl;

LastDerived ldr;


cout<<"객체생성 후 ... "<<endl;

ldr.ComplexFunc();


return 0;

}



이렇듯, 가상으로 Base 클래스를 상속하는 두 클래스를 다음과 같이 다중으로 상속하게 되면,


class LastDerived : public MiddleDerived_1, public MiddleDerived_2

{    };


LastDerived 객체 내에는 MiddleDerived_1 클래스와 MiddleDerived_2 클래스가 동시에 상속하는 Base 클래스의 멤버가 하나씩만 존재하게 된다.!!


- 위 예제에서 SimpleFunc 함수를 이름만 가지고 호출할 수 있을 것이다. 

그리고 예제의 실행결과를 보면 실제로 Base 클래스의 생성자가 한번만 호출되는 것을 확인할 수 있다.

만약에 가상 상속을 하지 않는다면, Base 클래스의 생성자는 두 번 호출된다.

Posted by scii
:

상속(inheritance)

Programming/C++ 2012. 6. 6. 20:19 |

상속은 적용이 중요한 문법이다. 적절할 때 선별적으로 적용할 수 있어야 한다.


#include <iostream>

#include <cstring>

using namespace std;


class PermenentWorker //데이터적 성격이 강한 클래스

{

    private:

        char name[100];

        int salary;


    public:

        PermenentWorker(char* name, int money)

            :salary(money)

        {

            strcpy(this->name, name);

        }

        int GetPay(void) const

        {

            return salary;

        }

        void ShowSalaryInfo(void) const

        {

            cout<<"name: "<<name<<endl;

            cout<<"salary: "<<GetPay()<<endl<<endl;

        }

};


class EmployeeHandler //기능적 성격이 강한 클래스(컨트롤 클래스)

{

    private:

        PermenentWorker* empList[50];

        int empNum;


    public:

        EmployeeHandler() :empNum(0)

{}

        void AddEmployee(PermenentWorker* emp) //새로운 직원정보 등록

        {

            empList[empNum++] = emp;

        }

        void ShowAllSalaryInfo(void) const         //모든 직원의 급여정보 출력

        {

            for(int i=0; i<empNum; i++)

                empList[i]->ShowSalaryInfo();

        }

        void ShowTotalSalary(void) const     //급여의 총액 출력

        {

            int sum=0;

            for(int i=0; i<empNum; i++)

                sum += empList[i]->GetPay();

            cout<<"salary sum: "<<sum<<endl;

        }

        ~EmployeeHandler()

        {

            for(int i=0; i<empNum; i++)

                delete empList[i];

        }

};


int main(void)

{

    EmployeeHandler handler;


    handler.AddEmployee(new PermenentWorker("asdf", 1000));

    handler.AddEmployee(new PermenentWorker("xcv", 2332));


    handler.ShowAllSalaryInfo();


    handler.ShowTotalSalary();

    return 0;

}



기능의 처리를 실제로 담당하는 클래스를 가리켜 '컨트롤(control) 클래스' 또는 '핸들러(handler) 클래스' 라 한다.


※ 컨트롤 클래스는 기능 제공의 핵심이 되기 때문에 모든 객체지향 프로그램에서 반드시 존재하는 클래스이다.




※ 멤버는 클래스가 정의될 때, 멤버의 초기화를 목적으로 정의된 생성자를 통해서 초기화하는 것이 가장 안정적이다. 그것이 비록 상속의 관계로 묶여있다 할지라도.


◆ 용어 정리 ◆


상위 클래스                                하위 클래스

기초(base) 클래스                      유도(derived) 클래스

슈퍼(super) 클래스                    서브(sub) 클래스

부모 클래스                               자식 클래스




#include <iostream>

using namespace std;


class Base

{

    private:

        int baseNum;

    public:

        Base(void) :baseNum(20)

        {

        cout<<"Base(void)"<<endl;

        }

        Base(int n) :baseNum(n)

        {

        cout<<"Base(int n)"<<endl;

        }

        void ShowBaseData()

        {

            cout<<baseNum<<endl;

        }

};


class Derived :public Base

{

    private:

        int derivedNum;

    public:

        Derived() :derivedNum(30)

        {

        cout<<"Derived()"<<endl;

        }

        Derived(int n) : derivedNum(n)

        {

        cout<<"Derived(int n)"<<endl;

        }

        Derived(int n1, int n2)

            : Base(n1), derivedNum(n2)

        {

            cout<<"Derived(int n1, int n2)"<<endl;

        }

        void ShowDerivedData()

        {

            ShowBaseData();

            cout<<derivedNum<<endl;

        }

};


int main(void)

{

    cout<<"case1......"<<endl;

    Derived dr1;

    dr1.ShowDerivedData();


    cout<<"case2........."<<endl;

    Derived dr2(12);

    dr2.ShowDerivedData();


    cout<<"case3..........."<<endl;

    Derived dr3(14, 15);

    dr3.ShowDerivedData();


    return 0;

}



위의 소스코드로 알 수 있는 점 두가지.

1. 유도 클래스의 객체 생성 과정에서 기초 클래스의 생성자는 100% 호출이 된다.

2. 유도 클래스의 생성자에서 기초 클래스의 생성자 호출을 명시하지 않으면, 기초 클래스의 void 생성자가 호출이 된다.


★ "클래스의 멤버는 해당 클래스의 생성자를 통해서 초기화해야 한다."




유도 클래스 객체의 소멸과정



위의 결과로 알 수 있는 사실.

1. 유도 클래스의 객체가 소멸될 때에는, 유도 클래스의 소멸자가 실행되고 난 다음에 기초 클래스의 소멸자가 실행된다.

2. 스택에 생성된 객체의 소멸순서는 생성순서와 반대이다.


※ 이러한 객체소멸의 특성 때문에 상속과 연관된 클래스의 소멸자는 "생성자에서 동적 할당한 메모리 공간은 소멸자에서 해제한다" 는 원칙을 지켜서 정의해야 한다.




#include <iostream>

#include <cstring>

using namespace std;


class Person

{

    private:

        char* name;

        

    public:

        Person(char* name)

        {

            this->name = new char[strlen(name)+1];

            strcpy(this->name, name);

        }

        ~Person()

        {

            delete []name;

        }

        void WhatYourName() const

        {

            cout<<"My name is "<<name<<endl;

        }

};


class UnivStudent : public Person

{

    private:

        char* major;


    public:

        UnivStudent(char* name, char* major)

            :Person(name)

        {

            this->major = new char[strlen(major)+1];

            strcpy(this->major, major);

        }

        ~UnivStudent()

        {

            delete []major;

        }

        void WhoAreYou() const

        {

            WhatYourName();

            cout<<"My major is "<<major<<endl<<endl;

        }

};


int main(void)

{

    UnivStudent st1("Jeon", "Houdini");

    st1.WhoAreYou();


    UnivStudent st2("Seo", "Maya");

    st2.WhoAreYou();


    return 0;

}



Posted by scii
: