fullstack

[Solidity] 상속, 다중상속, 함수 오버라이드(override)

Web3/Solidity

Solidity의 contract는 객체지향언어에서의 class와 닮은 점이 많습니다

그 중 상속에 대해서도 매우 유사한 형태를 보입니다

 

상속

상속은 다음과 같이 사용하면 됩니다

contract Doge {
  function catchphrase() public returns (string) {
    return "So Wow CryptoDoge";
  }
}

contract BabyDoge is Doge {
  function anotherCatchphrase() public returns (string) {
    return "Such Moon BabyDoge";
  }
}

contract 이름 뒤에 is 키워드를 이용하여 뒤에 상속받을 부모 contract의 이름을 써주면 됩니다

그럼 자식 contract는 상속받은 부모 contract의 모든 public 변수와 함수에 접근이 가능합니다

 

다중 상속

다중상속은 다음과 같이 사용하면 됩니다

contract MomDoge {
  function catchphrase() public returns (string) {
    return "So Wow CryptoDoge";
  }
}

contract DadDoge {
  function catchphrase() public returns (string) {
    return "So Wow CryptoDoge";
  }
}

contract BabyDoge is MomDoge, DadDoge {
  function anotherCatchphrase() public returns (string) {
    return "Such Moon BabyDoge";
  }
}

이미 상속받은 부모 contract가 있다면 뒤에 ','를 붙여 구분하여 다중상속을 할 수 있습니다

 

함수 오버라이드

상속을 받으면 부모의 public함수에 접근이 가능합니다

이때 부모의 함수의 이름과 같은 이름과 매개변수를 사용하여 함수를 재정의 하고 싶을 때가 있습니다

이를 함수 오버라이드(Override)라고 합니다

 

기본적인 단일상속에서는 다음과 같이 사용하면 됩니다

contract MomDoge {
  function catchphrase() public virtual returns (string) {
    return "So Wow CryptoDoge";
  }
}

contract BabyDoge is MomDoge {
  function catchphrase() public override returns (string) {
    return "Such Moon BabyDoge";
  }
}

부모 contract의 함수에서는 virtual을 자식 contract의 함수에서는 override를 명시해 주면 됩니다

 

만약 다중상속이고 오버라이드 하려는 함수가 여러 부모가 가지고 있는 함수라면

다음과 같이 괄호를 사용하여 해당 함수를 가진 contract들을 명시해 주어야 합니다

 

contract MomDoge {
  function catchphrase() public virtual returns (string) {
    return "So Wow CryptoDoge";
  }
}

contract DadDoge {
  function catchphrase() public virtual returns (string) {
    return "So Wow CryptoDoge";
  }
}

contract BabyDoge is MomDoge, DadDoge {
  function catchphrase() public override(MomDoge, DadDoge) returns (string) {
    return "Such Moon BabyDoge";
  }
}

[C++] 상속의 조건. is-a 와 has-a

Language/C++

상속의 조건에는 is-a 관계에 의한 상속과 has-a 관계에 의한 상속이 있다.




- is-a 관계에 의한 상속


is-a 관계에 의한 상속은 "~은 ~이다." 가 성립되는 관계를 말한다. 예를 들어 보자.


" A student is a person. " (학생은 사람이다)


위의 예에서 student가 Derived 클래스, person이 Base 클래스가 되는 것이다. 

Derived 클래스는 Base 클래스의 모든 멤버를 상속받기 때문에  모든 student는 person의 특징을 지니게 되는 것이다.

만일 반대로 "A person is a student." 라고 한다면 모든 사람이 학생은 아니기 때문에 이는 성립하지 않는다.


위 예를 코드로 나타내면 다음과 같다.


1
class Person : public Student





- has-a 관계에 의한 상속


has-a 관계에 의한 상속은 "~은 ~을 소유한다." 가 성립되는 관계를 말한다. 예를 들어 보자.


" The student have a book. " (학생은 책을 소유한다)


위의 예는 다음과 같이 코드로 나타낼 수 있다.


1
class Student : public Book


이처럼 상속은 소유를 표현하기 위해서도 사용될 수 있다.




- has-a 관계에 의한 포함


위에서 소유를 표현하기 위해서 상속을 사용하였는데, 이는 포함을 통해서도 표현할 수 있다.


1
2
3
4
5
6
class Student
{
    Book book;
public:
    void readBook() { book.read(); }
};



클래스 객체 멤버로 Book을 Student 클래스 안에서 선언하였다.
위에 예제처럼 상속을 사용하지 않고도 소유를 표현할 수 있다.

그럼 어떤 것이 좋은 방법이라고 할 수 있을까?
상황에 따라 다르겠지만 상속에 의해 소유를 표현하게 되면 결합도가 높아지게 된다.(객체지향에서 결합도는 낮을수록 좋다)
따라서 has-a 관계에서는 상속 관계 보다는 포함 관계를 사용하는 것이 좋다고 할 수 있다.


'Language > C++' 카테고리의 다른 글

[C++] 오버라이딩 (Overriding)  (0) 2015.12.24
[C++] 객체 포인터  (0) 2015.12.22
[C++] 상속 (Inheritance)  (0) 2015.12.03
[C++] mutable 키워드  (0) 2015.12.03
[C++] explicit 키워드  (0) 2015.12.02

[C++] 상속 (Inheritance)

Language/C++

- 상속의 기본 개념


현실 세계에서 부모의 재산을 물려받는 상속과 비슷하게 객체지향에서의 상속은 다른 클래스의 모든 멤버를 물려 받아 지니고 있게 되는 것을 말한다.

이때 상속되는 클래스를 'Base 클래스' 라고 하며, 상속하는 클래스를 'Derived' 클래스라한다. 다른 표현으로 다른 표현으로 부모 클래스와 자식 클래스 혹은 Super 클래스와 Sub 클래스 라고도 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
class AAA
{
public:
    void funcA() { cout << "AAA" << endl; }
};
 
class BBB : public AAA
{
public:
    void funcB() { 
        funcA();
        cout << "BBB" << endl;
    }
};



위 코드에서 7번째 줄을 보면 콜론(:)을 이용하여 BBB 클래스가 AAA클래스를 public 상속한 것을 볼 수 있다.

따라서 AAA 클래스의 멤버 함수인 funcA를 BBB클래스에서 자유롭게 쓰고 있는 것을 볼 수 있다.




- 상속하는 클래스의 객체 생성 및 소멸 순서


객체의 생성과 소멸순서를 알아보기 위해 다음과 같이 출력을 해보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
class AAA
{
public:
    AAA() { cout << "AAA Constructor" << endl; }
    ~AAA() { cout << "AAA Destructor" << endl; }
};
 
class BBB : public AAA
{
public:
    BBB() { cout << "BBB Constructor" << endl; }
    ~BBB() { cout << "BBB Destructor" << endl; }
};




위와 같이 생성자는 Base 클래스가 먼저 출력되고 소멸자는 반대로 출력되는 것을 볼 수 있다.

소멸자는 반대로 호출되는 이유는 메모리의 완벽한 해제를 위해서이다.


여기서 중요한것은 생성자가 호출되는 과정인데, 출력만 보면 AAA 클래스의 생성자가 먼저 호출되고 BBB 클래스의 생성자가 호출되는 것 같지만, 사실은 그 반대다.

BBB 클래스의 생성자가 먼저 호출되고 몸체부분은 실행되지 않는다. 다음으로 AAA 클래스의 생성자가 실행되고 나서야 BBB의 몸체부분이 실행되는 것이다.

이걸 제대로 이해한다면 멤버 이니셜라이저(member initializer)를 이용하여 Base 클래스에서 인자가 존재하는 생성자를 올바르게 초기화 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
class AAA
{
    int valA;
public:
    AAA(int _val) { valA = _val; }
 
};
 
class BBB : public AAA
{
public:
    BBB(int _val) : AAA(_val) {}
};



12번째 줄에서 멤버 이니셜라이저를 통해 AAA 클래스의 인자가 있는 생성자를 호출하였다.




- public, protected, private 상속


기본적으로 public 상속을 많이 사용하지만 세가지 형태의 상속이 존재한다.

이 상속 형태에 따라 Base 클래스의 멤버가 Derived 클래스로 상속되는 과정에서 접근 권한이 변경된다.

상속되는 과정에 따른 접근 권한 변경은 아래와 같다.



'Language > C++' 카테고리의 다른 글

[C++] 객체 포인터  (0) 2015.12.22
[C++] 상속의 조건. is-a 와 has-a  (0) 2015.12.19
[C++] mutable 키워드  (0) 2015.12.03
[C++] explicit 키워드  (0) 2015.12.02
[C++] static 키워드  (0) 2015.12.02