fullstack

[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

[C++] mutable 키워드

Language/C++

원래대로라면 const로 멤버 함수가 상수화되면 이 함수는 멤버 변수를 변경시키지 못한다. 그러나 멤버 변수가 mutable로 선언되어 있다면 상수화된 멤버 함수라도 멤버 변수 변경이 가능해진다.

mutable 선언은 되도록 사용하지 않는 것이 좋다고 한다. 어쩔수 없는 경우라면 차라리 함수를 상수화하지 않는 것이 더 나을 수 있다.

아래는 mutable 키워드 사용 예이다.


1
2
3
4
5
6
7
8
9
class AAA
{
    mutable int val;
public:
    void setValue(int _val) const
    {
        val = _val;
    }
};



setValue는 상수화된 멤버 함수이므로 원래대로라면 멤버 변수 val을 변경할 수 없지만 val 이 mutable로 선언되었으므로 값 수정이 가능하다.


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

[C++] 상속의 조건. is-a 와 has-a  (0) 2015.12.19
[C++] 상속 (Inheritance)  (0) 2015.12.03
[C++] explicit 키워드  (0) 2015.12.02
[C++] static 키워드  (0) 2015.12.02
[C++] const 키워드  (0) 2015.12.02

[C++] explicit 키워드

Language/C++

explicit 키워드는 생성자 앞에 붙여 묵시적인 호출을 허용하지 않을 때 사용 된다.

즉 객체 생성 관계를 분명히 하고자 할 경우 사용하며, 모든 생성자의 explicit 키워드를 붙이는 프로그래머도 많다.

아래는 explicit 키워드 사용 예이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
class AAA
{
public:
    explicit AAA(int n) { cout << n << endl;}
};
 
int main()
{
    AAA a1(10);
    AAA a2 = 10// error
 
    return 0;
}



AAA a2 = 10; 은 묵시적으로 AAA a2(10)  으로 변환되나 explicit 키워드로 묵시적인 호출을 허용하지 않았으므로 에러가 나타난다.


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

[C++] 상속 (Inheritance)  (0) 2015.12.03
[C++] mutable 키워드  (0) 2015.12.03
[C++] static 키워드  (0) 2015.12.02
[C++] const 키워드  (0) 2015.12.02
[C++] 복사 생성자  (0) 2015.12.02

[C++] static 키워드

Language/C++

C언어에서 static 키워드는 지역 변수에 붙여 전역 변수의 특징을 지니게 하는데 사용되었다.

하지만 객체지향에서는 전역 변수, 전역 함수라는 개념이 존재하지 않는다. 따라서 이를 대처하기 위해 static 멤버라는 개념이 등장하였다.

다음은 static 멤버 사용 예이다.


1
2
3
4
5
6
7
8
class AAA
{
    static int count;
public:
    AAA() { cout << count++ << "번째 객체 생성." << endl; }
};
 
int AAA::count = 1;



static 멤버는 main함수가 호출되기도 전에 메모리 공간에 올라가 초기화되므로 public으로 선언이 된다면 객체 생성 이전에도 접근이 가능하다.

또한 객체의 멤버로 존재하는 것이 아니라 선언되어 있는 클래스 내에서 직접 접근할 권한이 부여된 것 뿐이다.


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

[C++] mutable 키워드  (0) 2015.12.03
[C++] explicit 키워드  (0) 2015.12.02
[C++] const 키워드  (0) 2015.12.02
[C++] 복사 생성자  (0) 2015.12.02
[C++] friend 선언  (0) 2015.12.02

[C++] const 키워드

Language/C++

const 키워드는 어디에 사용하는가에 따라 다양하게 활용된다.




- const 키워드 기본


const 키워드의 가장 기본적인 사용 3가지는 아래와 같다.



1. 변수를 상수화 한다.


1
2
const int a = 10;
= 20// error



2.  포인터가 가리키는 데이터를 상수화 한다.


1
2
3
int a = 10;
const int* pa = &a;
*pa = 20// error



3. 포인터 자체를 상수화 한다.


1
2
3
4
int a = 10;
int b = 20;
int* const pa = &a;
pa = &b; // error





- 멤버 이니셜라이저 (member initiallizer)


클래스에서 변하지 않는 상수로 멤버변수를 초기화 하고 싶을 때가 있다. 하지만 const로 멤버 변수를 선언 후 생성자에서 조작하려고 할 경우 이미 쓰레기값을 초기값으로 상수화 되었기 때문에 생성자를 통한 초기화를 사용할 수 없다. 

이럴 때 사용하는 것이 멤버 이니셜라이저라는 문법이다. 이것을 사용하면 const 멤버 변수를 초기화할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
class Info
{
    const int id;
    int password;
public:
    Info(int _id, int _password) : id(_id)
    {
        password = _password;
    }
};



위와같으 콜론(:)을 이용하여 const 멤버변수를 초기화 할 수 있으며 콤마(,)를로 구분지어 여러 const 멤버 변수를 초기화 할 수 있다.




- const 멤버 함수


멤버 함수를 선언할 때 const 키워드를 붙여 멤버 함수를 상수화 할 수 있다.

멤버 함수가 상수화되면 이 함수를 통해서 멤버 변수의 값이 변경되는 것은 허용되지 않는다.

다음과 같이 사용할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Info
{
    const int id;
    int password;
public:
    Info(int _id, int _password) : id(_id)
    {
        password = _password;
    }
 
    void showData() const
    {
        cout << "id: " << id << endl;
        cout << "password: " << password << endl;
    }
};



이렇게 사용하면 showData라는 정보를 출력해주기만 하는 함수에서 프로그래머의 실수로 멤버 변수의 값을 변경하는 것을 방지할 수 있다.

또한, 상수화된 함수는 상수화되지 않은 함수의 호출을 허용하지 않을 뿐만 아니라, 멤버 변수의 포인터를 리턴하는 것도 허용하지 않는다.




- const 객체


객체또한 생성과 동시에 상수화 하는 것이 가능하다.

객체가 상수화되면 멤버변수의 조작이 불가능해지며, 상수화된 멤버 함수만 호출이 가능해진다.


1
const AAA a;





- const 키워드를 써야하는 이유


const 키워드는 쓰나 안쓰나 동작은 똑같기 때문에 별로 필요없다고 생각할 수 있으나, 프로그래머의 실수를 줄여주고 프로그램의 안정성을 높여준다.

만약 프로그래머의 실수로 잘못된 코드가 들어갔지만 컴파일에는 이상이 없어 에러메세지를 받을 수 없다면 이는 매우 치명적이다.

그러므로 프로그램을 작성할때 const 키워드를 가급적 많이 사용하는 것이 좋다.


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

[C++] explicit 키워드  (0) 2015.12.02
[C++] static 키워드  (0) 2015.12.02
[C++] 복사 생성자  (0) 2015.12.02
[C++] friend 선언  (0) 2015.12.02
[C++] this 포인터  (0) 2015.12.02

[C++] 복사 생성자

Language/C++

- 복사 생성자란


복사 생성자란 자기 자신과 같은 형태의 객체를 인자로 받을 수 있는 생성자를 말한다.


다음은 복사 생성자의 예이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;
 
class AAA
{
    int val;
public:
    AAA(int _val) {
        val = _val;
    }
    AAA(const AAA& a) {
        val = a.val;
    }
    void showValue() { cout << "value: " << val << endl; }
};
 
int main()
{
    AAA a1(10);
    AAA a2(a1);
 
    a1.showValue();
    a2.showValue();
 
    return 0;
}




복사생성자는 인자로 전달된 객체를 레퍼런스로 받아야 하며 내용 변경을 불가능 하게 하기 위해 const 선언을 한다.




- 다폴트 복사 생성자


일반 생성자와 소멸자처럼 복사 생성자도 디폴트 복사 생성자를 제공한다.

위 예제에서 11~13 라인을 지워도 자동으로 디폴트 복사 생성자를 삽입하기 때문에 똑같이 동작한다.


하지만 생성자내에서 동적할당을 하는경우 디폴트 복사 생성자에서는 얕은 복사(Shallow Copy)를 하게 되는데 이경우 문제가 발생한다.

따라서 깊은 복사(Deep Copy) 로 직접 복사 생성자를 구현해 주어야 한다.


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

[C++] static 키워드  (0) 2015.12.02
[C++] const 키워드  (0) 2015.12.02
[C++] friend 선언  (0) 2015.12.02
[C++] this 포인터  (0) 2015.12.02
[C++] 생성자(Constructor)와 소멸자(Destructor)  (0) 2015.11.30

[C++] friend 선언

Language/C++

friend는 말그대로 친구를 말한다. 친구에게는 모든것을 허용해야 한다.

클래스안에서 private로 선언된 멤버변수는 외부 접근이 허용되지 않는 것이 원칙이다. 하지만 friend 선언을 통해서 접근을 허용할 수 있다.




- 전역 함수에 대한 friend 선언

 

1
2
3
4
5
6
7
8
9
10
11
class AAA
{
    int val;
public:
    friend void setValue(AAA& a, int val); // friend 선언
};
 
void setValue(AAA& a, int val)
{
    a.val = val;
}



원래대로라면 전역 함수 setValue에서 AAA객체의 val값에 접근할 수 없다.

하지만 5번째줄에서 friend 선언을 했으므로 접근이 허용되었다.




- 클래스에 대한 friend 선언

 

1
2
3
4
5
6
7
8
9
10
11
12
13
class AAA
{
    int val;
    friend class BBB;
};
 
class BBB
{
public:
    void setValue(AAA& a, int val) {
        a.val = val;
    }
};



클래스도 마찬가지로 friend 선언을 하면 private영역의 접근을 허용한다.

주의할 것은 friend 선언은 단방향적으로 AAA 클래스가 BBB 클래스에게 접근을 허용한 것이지 BBB 클래스또한 AAA 클래스에게 접근을 허용한 것은 아니다.




friend 선언은 매우 편리하지만 객체지향에서 중요시하는 '정보 은닉'을 해치는 경우를 초래할 수도 있으니 너무 많은 남발은 하지 않는 것이 좋다.


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

[C++] const 키워드  (0) 2015.12.02
[C++] 복사 생성자  (0) 2015.12.02
[C++] this 포인터  (0) 2015.12.02
[C++] 생성자(Constructor)와 소멸자(Destructor)  (0) 2015.11.30
[C++] 캡슐화 (EnCapsulation)  (0) 2015.11.30

[C++] this 포인터

Language/C++

this 포인터는 자기참조 포인터이다. 즉, 자기 자신을 가리키는 용도로 사용된다.

만약 0x00번지에 할당된 객체의 멤버함수 내에서 this를 사용한다면, 0x00번지를 의미하는 포인터가 된다.


다음은 this를 포인터를 활용한 예이다.


1
2
3
4
5
6
7
8
9
class AAA
{
    int val;
public:
    AAA(int val)
    {
        this->val = val;
    }
};



위는 객체의 멤버변수와 생성자의 매개변수가 같은 이름을 사용하고 있다. 원래대로라면 어떤 변수이건간에 지역변수보다 우선시 될 수 없으므로 멤버변수에 접근할 수 없다.

하지만 this로 객체의 주소값을 가르켜 멤버변수에 접근하였다.

물론 매개변수와 멤버변수의 이름을 다르게 하면 쉽게 해결되지만 생각해야할 변수의 이름이 하나 줄어든 것 만으로도 큰 의미라고 할 수 있다.


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

[C++] 복사 생성자  (0) 2015.12.02
[C++] friend 선언  (0) 2015.12.02
[C++] 생성자(Constructor)와 소멸자(Destructor)  (0) 2015.11.30
[C++] 캡슐화 (EnCapsulation)  (0) 2015.11.30
[C++] 정보은닉 (Information Hiding)  (0) 2015.11.30

[C++] 생성자(Constructor)와 소멸자(Destructor)

Language/C++

- 생성자 (Constructor)


생성자는 객체를 생성과 동시에 초기화 할 수 있는 메커니즘이다.

모든 객체는 생성이 되면 메모리를 할당받고 생성자를 호출하게 된다.

생성자는 함수이고 ,클래스의 이름과 같은 이름을 지니며, 리턴이 없다.



1
2
3
4
5
6
class AAA
{
    int num;
public:
    AAA()    { num = 10; }
};



위와 같이 생성자를 이용하여 객체의 멤버변수를 초기화 할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
class AAA
{
    int num;
public:
    AAA(int n) { num = n; }
};
 
int main()
{
    AAA a(10);
 
    return 0;
}



또한 위와 같이 매개변수를 이용하여 초기화 할 수 있으며 오버로딩도 가능하다.




- 소멸자 (Destructor)


경우에 따라 객체가 소멸되기 전에 처리해야 할 작업이 있을 수 있다. (예를 들어 객체안에서 동적할당을 사용한 경우 메모리 해제)

소멸자는 그러한 일들을 처리하기 위한 메커니즘이다.

모든 객체는 소멸자를 호출하고 메모리 반환후 소멸하게 된다.

소멸자는 함수이고, 클래스의 이름 앞에 ~가 붙은 형태의 이름을 가지며, 리턴이 없다. 또한 생성자와 다르게 매개변수를 받을 수 없다.


1
2
3
4
5
6
7
class AAA
{
    int *arr;
public:
    AAA(int size) { arr = new int[size]; }
    ~AAA() { delete []arr; }
};



위와 같이 소멸자를 이용하여 동적할당받은 메모리를 해제해 줄 수 있다.




- 디폴트 생성자 , 디폴트 소멸자


생성자와 소멸자는 따로 정의하지 않으면 디폴트 생성자와 디폴트 소멸자가 자동으로 삽입되며, 둘은 아무런 일도 하지 않는다.

자동으로 삽입된 디폴트 생성자와 디폴트 소멸자는 아래와 같다.


1
2
3
4
5
6
7
class AAA
{
    int num;
public:
    AAA() {}
    ~AAA() {}
};



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

[C++] friend 선언  (0) 2015.12.02
[C++] this 포인터  (0) 2015.12.02
[C++] 캡슐화 (EnCapsulation)  (0) 2015.11.30
[C++] 정보은닉 (Information Hiding)  (0) 2015.11.30
[C++] 동적할당. new와 delete  (0) 2015.11.30