fullstack

[C++] 연산자 오버로딩

Language/C++

연산자 오버로딩은 일종의 C++와의 약속이다. operator라는 키워드와 연산자 기호를 묶어서 함수 이름을 정의하면 연산자를 이용해 함수를 호출하는 셈이다.

즉, p+10이라는 문장이 있고, p가 객체라면 p.operator+(10) 이라는 문장으로 해석하여 오버로딩한 함수를 호출하게 되는 것이다.


다음은 간단한 연산자 오버로딩의 예제이다.


1
2
3
4
5
6
7
8
9
10
11
class Point {
private:
    int x, y;
public:
    Point(int _x=0int _y=0) : x(_x), y(_y) {}
    Point operator+(int val);
};
Point Point::operator+(int val) {
    x+=val;
    y+=val;
}



위와 같이 연산자 오버로딩을 하면 다음과 같이 간단한 방법으로 포인트 객체의 x, y값을 한번에 증가시킬 수 있다.


1
2
Point p;
p+10;



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

[C++] 템플릿 (Template)  (0) 2016.01.10
[C++] 임시 객체  (0) 2016.01.04
[C++] 다중 상속  (1) 2015.12.28
[C++] virtual 소멸자  (0) 2015.12.26
[C++] 순수 가상 함수와 추상 클래스  (0) 2015.12.26

[C++] 다중 상속

Language/C++

다중 상속이란 하나의 Derived 클래스가 둘 이상의 Base 클래스를 상속하는 것을 말한다.

다음과 같이 콤마 연산자로 사용할 수 있다.


1
class CCC : public AAA, public BBB



다중 상속은 유용한 문법이 아니다. 클래스의 관계를 복잡하게 만들기 때문에 사용하지 않는 것이 좋다.


아래는 다중 상속을 사용할 때 발생할 수 있는 문제점이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AAA
{
public:
    void func() { cout << "AAA" << endl; }
};
 
class BBB
{
public:
    void func() { cout << "BBB" << endl; }
};
 
class CCC : public AAA, public BBB
{
public:
    void print() {
        func(); // error
    }
};



위 예제는 다중 상속을 사용했을 때 발생할수 있는 에러이다.

CCC 클래스는 AAA 클래스와 BBB 클래스를 상속하였는데 두 클래스에 같은 이름의 멤버 함수가 있다면 이처럼 모호성이 발생한다.

컴파일러는 func 함수가 AAA 클래스에 있는 것을 말하는 건지 BBB 클래스에 있는 것을 말하는 건지 모호하여 에러를 출력하였는데, 이는 AAA::func() 혹은 BBB::func() 이런 식으로 클래스를 알려주면 컴파일이 가능하다.




다음은 또다른 문제점 이다.


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
class AAA
{
public:
    void func1() { cout << "AAA" << endl; }
};
 
class BBB : public AAA
{
public:
    void func2() { cout << "BBB" << endl; }
};
 
class CCC : public AAA
{
public:
    void func3() { cout << "CCC" << endl; }
};
 
class DDD : public BBB, public CCC
{
public:
     void print() {
         func1(); // error
    }
};




BBB 클래스와 CCC 클래스 둘다 AAA 클래스를 상속하고 있고, DDD 클래스는 BBB 클래스와 CCC 클래스를 다중 상속하고 있다.

아래 그림 처럼 다중 상속이 다이야몬드 형태로 구조가 나타날때도 모호성이 발생한다.



BBB 클래스와 CCC 클래스 둘다 AAA 클래스를 상속하였기 때문에 이 둘을 다중 상속하게되면 AAA 클래스를 중복으로 상속하게 되는 것이다. 때문에, AAA 클래스의 멤버 함수를 두 개 지니게 되고, 모호성이 발생하여 에러가 발생하게 된다.

이는, AAA 클래스가 DDD 클래스에 한번만 상속되도록 하는 virtual 상속을 사용하여 해결할 수 있다.


1
class BBB : virtual public AAA



1
class CCC : virtual public AAA



이렇게 BBB 클래스와 CCC 클래스에서 virtual 상속을 하게되면 이 두 클래스를 다중상속 하더라도 AAA 클래스 안에 존재하는 멤버들은 한번만 상속이 이루어 진다.




이처럼 다중상속의 문제점을 해결할 수는 있지만, 어쨋든 다중 상속을 사용하면 클래스들 간에 관계가 복잡해지므로 사용하지 않는 것이 좋다.



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

[C++] 임시 객체  (0) 2016.01.04
[C++] 연산자 오버로딩  (0) 2016.01.04
[C++] virtual 소멸자  (0) 2015.12.26
[C++] 순수 가상 함수와 추상 클래스  (0) 2015.12.26
[C++] static binding & dynamic binding  (0) 2015.12.26

[C++] virtual 소멸자

Language/C++

이전 글인 [C++] virtual 키워드 에서 virtual 키워드로 가상함수를 만든다는 것을 알았다.

하지만 virtual 키워드를 붙여 주어야 할 대상은 가상함수 뿐만이 아니다. 소멸자 앞에 virtual 키워드를 붙인 것을 virtual 소멸자라 한다.

그럼 virtual 소멸자는 왜 써야할까? 다음 예제를 보자

 

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
#include <iostream>
using namespace std;
 
class AAA
{
public:
    AAA() { cout << "AAA()" << endl; }
    ~AAA() { cout << "~AAA()" << endl; }
};
 
class BBB : public AAA
{
public:
    BBB() { cout << "BBB()" << endl; }
    ~BBB() { cout << "~BBB()" << endl; }
};
 
int main()
{
    AAA* b = new BBB;
 
    delete b;
 
    return 0;
}




위의 예제를 보면 AAA 타입의 포인터로 BBB 객체를 생성하였다. 그리고 해당 객체를 소멸하자 AAA 클래스의 소멸자만 호출이 되었다.

이는 소멸할 객체는 BBB 객체이지만 AAA 타입의 포인터로 가리키고 있기 때문에, 컴파일러는 AAA 객체로 인식한 것이다.


이를 해결하기 위해서 다음과 같이 Base 클래스의 소멸자에 virtual 키워드를 붙여주면 모든 소멸자가 올바르게 출력되는 것을 볼 수 있다.


1
virtual ~AAA() { cout << "~AAA()" << endl; }



이렇게 하면 Base 클래스의 소멸자를 호출하려다 virtual 인 것을 보고, Derived 클래스의 소멸자를 호출하게 된다.

그 다음으로 BBB 클래스의 소멸자는 AAA 클래스를 상속하고 있기 때문에 다시 AAA 클래스이 소멸자가 호출되게 된다.


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

[C++] 연산자 오버로딩  (0) 2016.01.04
[C++] 다중 상속  (1) 2015.12.28
[C++] 순수 가상 함수와 추상 클래스  (0) 2015.12.26
[C++] static binding & dynamic binding  (0) 2015.12.26
[C++] virtual 키워드  (0) 2015.12.24

[C++] 순수 가상 함수와 추상 클래스

Language/C++

이전 글인 [C++] virtual 키워드 를 보면 virtual 키워드를 통해서 가상 함수를 정의하였다.

보통 가상 함수를 쓸 때, Base 클래스에서는 함수를 정의만 하고 실제 구현은 오버라이딩 하고 있는 Derived 클래스에서 하게된다.

이러한 경우 다음과 같이 함수를 선언만 하고 정의를 하지 않아도 된다.


1
virtual void func() = 0;



이처럼 선언한 함수를 순수 가상 함수라 부른다.

이처럼 Base 클래스에서 순수 가상 함수를 정의하면 Derived 클래스에서는 반드시 오버라이딩 하여 함수를 정의 해야 한다.

순수 가상 함수는 진짜로는 존재하지 않는 가상의 함수를 말하며, 하나 이상의 순수 가상 함수를 가지고 있는 클래스를 추상(abstract) 클래스라 부른다.

추상 클래스는 함수 정의가 생략된 순수 가상 함수를 가지고 있기 때문에 완전한 클래스가 아니다. 

따라서 추상 클래스는 객체화 할 수 없다. (객체 지향적 설계에 따른다면 객체화 할 필요도 없다)


순수 가상함수와 추상 클래스가 필요한 이유는 다음 예제를 보면 이해할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Object
{
public:
    virtual void draw() = 0;
};
 
class Circle : public Object
{
public:
    void draw() { cout << "draw circle" << endl; }
};
 
class Rect : public Object
{
public:
    void draw() { cout << "draw rect" << endl; }
};



Object 클래스는 draw라는 순수 가상 함수를 가진 추상 클래스로, 이를 상속하는 클래스는 모두 draw라는 함수를 오버라이딩하여 정의 해야 한다.

이처럼 순수 가상 함수를 이용해 모든 Derived 클래스에서 구현해야될 함수를 정의할 수 있다.


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

[C++] 다중 상속  (1) 2015.12.28
[C++] virtual 소멸자  (0) 2015.12.26
[C++] static binding & dynamic binding  (0) 2015.12.26
[C++] virtual 키워드  (0) 2015.12.24
[C++] 오버라이딩 (Overriding)  (0) 2015.12.24

[C++] static binding & dynamic binding

Language/C++

binding이란 프로그램 소스에 쓰인 내부 요소, 이름, 식별자들에 대해 값이나 속성을 확정하는 과정을 일컫는 말이다.

이 과정이 컴파일 타임에 이루어지면 static binding이라 하고, 런 타임에 이루어지면 dynamic binding이라 한다.




1
int static_binding = 1;



static binding은 위와 같이 일반적으로 이루어진다.

데이터 타입이 int로 정해지는 것과 그 타입의 변수명이 static_binding으로 정해지는 것이 바로 static binding이다.




1
2
AAA* a = new /* ??? */ ;
a->func();



dynamic binding은 이전 글인 [C++] virtual 키워드 에서 이미 사용하였다.

Base 클래스의 멤버 함수를 가상함수로 선언하여 여러 Derived 클래스에서 오버라이딩 한다면 위의 ??? 부분에 어떤 Derived 클래스를 넣는 가에 따라서 다른 함수가 호출된다.

이처럼 dynamic binding은 포인터가 가리키는 객체가 무엇인가에 따라서 호출되는 함수가 유동적으로 변하게 된다.




dynamic binding은 다형성의 한 예이며, 이처럼 모습은 같은데 형태는 다른것을 다형성(polymorphism)이라 한다.


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

[C++] virtual 소멸자  (0) 2015.12.26
[C++] 순수 가상 함수와 추상 클래스  (0) 2015.12.26
[C++] virtual 키워드  (0) 2015.12.24
[C++] 오버라이딩 (Overriding)  (0) 2015.12.24
[C++] 객체 포인터  (0) 2015.12.22

[C++] virtual 키워드

Language/C++

virtual 키워드를 사용하면 오버라이딩 되는 함수를 가상으로 선언할 수 있다.

함수의 앞에 virtual 키워드를 붙여주면 되고, 이를 가상 함수(virtual function)라 부른다.


이전 글인 [C++] 오버라이딩 (Overriding) 의 예제에서 AAA 클래스의 함수만 가상함수로 바꾸어 보았다.


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
#include <iostream>
using namespace std;
 
class AAA
{
public:
    virtual void func() { cout << "AAA" << endl; }
};
 
class BBB : public AAA
{
public:
    void func() { cout << "BBB" << endl; }
};
 
int main()
{
    BBB b;
    b.func();
 
    AAA* a = new BBB;
    a->func();
 
    return 0;
}




AAA 클래스의 func 함수는 가상함수로 선언하였기 때문에 존재하지만 존재하지 않는 함수로 선언되어 있는 것이다. 따라서, AAA 타입의 포인터로 접근하더라도 호출되지 않고 오버라이딩된 BBB 클래스의 func 함수가 호출되는 것이다.


가상 함수의 특성은 상속된다. 따라서, Base 클래스에서 선언한 가상 함수를 Derived 클래스에서 오버라이딩 한다면 이 오버라이딩한 함수 또한 가상 함수의 특성을 가지게 된다.

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

[C++] 순수 가상 함수와 추상 클래스  (0) 2015.12.26
[C++] static binding & dynamic binding  (0) 2015.12.26
[C++] 오버라이딩 (Overriding)  (0) 2015.12.24
[C++] 객체 포인터  (0) 2015.12.22
[C++] 상속의 조건. is-a 와 has-a  (0) 2015.12.19

[C++] 오버라이딩 (Overriding)

Language/C++

상속 관계에서 Base 클래스에서 선언한 멤버는 Derived 클래스에서 물려받는다고 하였다.

이때 Derived 클래스에서 Base 클래스에서 선언된 멤버를 다시 선언하여 재정의 하는것을 오버라이딩(Overriding)이라고 한다.

다음은 오버라이딩 예제이다.


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
#include <iostream>
using namespace std;
 
class AAA
{
public:
    void func() { cout << "AAA" << endl; }
};
 
class BBB : public AAA
{
public:
    void func() { cout << "BBB" << endl; }
};
 
int main()
{
    BBB b;
    b.func();
 
    AAA* a = new BBB;
    a->func();
 
    return 0;
}




위의 예제를 보면 AAA 클래스에서 func라는 함수를 정의하였고 AAA 클래스를 상속하는 BBB 클래스에서 func 함수를 다시 정의해 오버라이딩 하였다.

BBB 클래스 객체인 b에서 func 함수를 호출하자 오버라이딩 이전에 정의된 AAA 클래스의 func 함수는 가려지고 BBB 클래스의 func 함수가 호출되는 것을 볼 수 있다.

하지만, 객체포인터를 이용하여 AAA 클래스를 가리킨다면 AAA 클래스에 정의된 func 함수가 호출되는 것을 볼 수 있다.


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

[C++] static binding & dynamic binding  (0) 2015.12.26
[C++] virtual 키워드  (0) 2015.12.24
[C++] 객체 포인터  (0) 2015.12.22
[C++] 상속의 조건. is-a 와 has-a  (0) 2015.12.19
[C++] 상속 (Inheritance)  (0) 2015.12.03

[C++] 객체 포인터

Language/C++

객체 포인터는 해당 객체의 주소값 뿐만 아니라 해당 클래스를 상속하는 Derived 클래스 객체의 주소값도 저장이 가능하다.


1
2
3
4
5
6
7
8
9
10
int main()
{
    Person* p1 = new Person;
    Person* p2 = new Student;
 
    p1->sleep();
    p2->sleep();
 
    return 0;
}



위의 예제는 Student가 Person을 상속하고 있을 때 객체 포인터 예제이다.

Person 과 Student는 is-a 관계에 의해서 Student는 Person이므로, Student 클래스의 객체는 Person 클래스의 객체이다.

따라서 Person 타입의 포인터로 Student 객체를 가리킬 수 있다.




1
2
3
4
5
6
7
8
9
10
int main()
{
    Person* p1 = new Person;
    Person* p2 = new Student;
 
    p1->sleep();
    //p2->study();    // error
 
    return 0;
}



위의 예제에서는 Student 클래스에만 존재하는 study 함수를 호출하려하자 에러가 발생하였다.

객체 포인터 권한의 특성때문인데, 해당 클래스의 객체 포인터는 가리키는 대상이 어떤 객체이건, 해당 클래스 타입 내에 선언된 멤버와 해당 클래스가 상속한 클래스의 멤버에만 접근이 가능하다.

즉, p2 객체는 Person 타입의 포인터이므로, Person 클래스 내에 선언된 멤버에만 접근이 가능하다.

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

[C++] virtual 키워드  (0) 2015.12.24
[C++] 오버라이딩 (Overriding)  (0) 2015.12.24
[C++] 상속의 조건. is-a 와 has-a  (0) 2015.12.19
[C++] 상속 (Inheritance)  (0) 2015.12.03
[C++] mutable 키워드  (0) 2015.12.03

안보이는 폴더 만들기

Tip

간단한 방법으로 안보이는 폴더를 만들수 있다.




1. 새폴더를 만든다.



2. 이름을 공백으로 바꾸어 준다.

공백은 ㄱ+한자+1 로 선택하면 된다.



3. 폴더의 속성 - 사용자 지정 - 아이콘 변경을 선택한다.



4. 아이콘 중 투명한 아이콘을 선택한다.





이렇게 하면 다음과 같이 폴더가 보이지 않게 된다.



드래그를 해보면 폴더가 있다는 것을 알 수 있다.



보안성은 별로 없지만 이렇게 간단한 방법으로 폴더를 숨겨볼 수 있다.

폴더 뿐만 아니라 다른 파일도 같은 방법으로 가능하다.

'Tip' 카테고리의 다른 글

가상 하드 디스크(VHD) 만들기  (0) 2015.11.30

[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