삽질의 현장/- C++

#025_시(c)시(c)해서 C++?!_멤버 이니셜라이저

shovelman 2015. 7. 23. 02:37

안녕하세요 삽잡이입니다...


이번 시간에는 기초로 다시 돌아가서 

이니셜라이즈에 대해서 썰을 풀어보고자 합니다...


이니셜라이즈... 언제 쓸까요? 두가지로 나뉩니다...

상수와 생성자에 관련되서 이니셜라이즈를 사용하게 되죠...

예시를 들어보겠습니다..


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
 
#include <iostream>
#include <string>
using namespace std;
 
class Member
{
    int num;
    string name;
};
 
void main()
{
    Member *sap = new Member();
}
 
 
cs

이와 같이 기본적인 Member class는 
생성자가 없으면 기본적으로 default 생성자가 자동으로 생성되어 문제없이 컴파일이 가능합니다...


하지만,


1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
 
class Member
{
    const int num;
    string name;
};
 
void main()
{
    Member *sap = new Member();
}
 
 
cs


이처럼 int형 변수 num에 const를 붙이면 적절한 기본 생성자가 없다며 에러를 발생시킵니다...

이 이유는 바로 num 변수는 상수이기 때문에 초기화를 해야한다고 나오는 것입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
 
class Member
{
    const int num;
    string name;
public:
    Member(int _num, string _name)
    {
        this->num = _num;
        this->name = _name;
    }
};
 
 
cs


자... 그럼 생성자를 만들습니다... 하지만 오류는 또 발생합니다...

num이라는 변수를 초기화해줘야하지만, 상수이기 때문에 초기화를 하지 못한다고 합니다...

당연한 말입니다... 

상수라고 정의를 한다면 그 값을 변경할 수 없게 되어 접근을 할 수 없게되죠...

또한 설령 접근이 가능하다고 해도...

this->num 즉, 클래스 멤버 변수인 num은 const화 되어있는 상수이기때문에 상수에 값을 넣을 수 없습니다.

좌항에는 변수가 와야지 상수가 올 수 없죠...


그렇다면 num이라는 const 변수는 값을 변경할 수 없는걸까요... 

아닙니다... 그래서 이때 이니셜라이저를 사용하는 것이죠...

const 변수도 값은 변하지 않지만 뭐... 값을 넣어주고서 변하지 못하게 하는게 당연하지 않습니까?


1
2
3
4
5
6
7
8
 
    
Member(int _num, string _name) :num(_num)
{
    this->name = _name;
}
 
 
cs


이렇게' : 멤버필드 (지역변수) ' 방식으로 상수를 초기화할 수 있습니다.

물론, 반드시 상수 멤버들만 이렇게 초기화가 가능한 것은 아닙니다...

다른 멤버들도 같은 방식으로 초기화가 가능하지만 선택사항인것이죠...


이렇게 많이들 사용하지는 않지만, 

이니셜라이저를 사용할 때, 괄호안에 메소드를 호출해도 상관 없음을 보여드립니다...


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 
    
class Member
{
    const int num;
    string name;
    int sap;
    int zape;
public:
    Member(int _num, string _name) :num(_num), sap(Sap()), zape(Zape(42))
    {
        this->name = _name;
    }
    int Sap()
    {
        return 0;
    }
    int Zape(int a, int b)
    {
        return a + b;
    }
};
 
 
cs


자... 상수 멤버를 초기화 하는 경우를 살펴봤습니다.

다음으로, 생성자에 관련한 이니셜라이저를 살펴보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 
    
class Member
{
    const int num;
    string name;
public:
    Member(int _num, string _name) :num(_num)
    {
        this->name = _name;
    }
};
 
class Sapzape
    :public Member
{
 
};
void main()
{
    Sapzape *sap = new Sapzape();
}
 
 
cs


... 너무 극단적인 예지만... 아무튼... 생성자에 대한 이니셜라이즈를 소개하고자합니다...


Sapzape라는 클래스는 Memeber 클래스를 상속받았습니다...

자... 그런데 Sapzape 모양의 객체를 생성하고자 했지만 오류가 납니다...

사용할 수 있는 적절한 기본 생성자가 없다면서 말입니다...


이상하군요...

class 내에 생성자가 없다면 default 생성자는 컴파일러에서 의해서 자동 생성되는 것이 아닌가....


예... 이상한게 아닙니다...

상속받은 클래스의 객체 생성이 이루어질 때에는 

반드시 기반 클래스 (부모 클래스)의 생성자를 우선적으로 호출하고

상속받은 클래스(파생 클래스)의 생성자를 다음으로 호출하게 됩니다...


즉, Member라는 부모 클래스에는 이미 생성자가 정의되어있는데

위와 같이 객체를 생성한다면 오류가 나는게 당연합니다.

전달할 인자들이 없으니... 생성자 호출을 해도 에러가 나게 되는 것입니다.


즉, 기반 형식의 생성자들이 기본 생성자가 없다면 상속 받은 파생 클래스에도 

기반형식에 맞춰서 생성자를 만들어줘야합니다.


음... 아직도 이해가 안가신다면 예시를 들어보도록 하겠습니다...


프로그래머라는 객체를 만들어 달라고 요청을 했습니다.


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 Job
{
    int salary;
    string name;
public:
    Job(int _salary, string _name)
    {
        this->salary = _salary;
        this->name = _name;
    }
};
 
class Programmer
    :public Job
{
 
};
void main()
{
    Programmer *sap = new Programmer();
}
 
 
cs


컴파일러는 기반 클래스인 Job 클래스에서 

이름과 월급을 전달 받아야된다는 명시가 되어 있는 생성자를 확인했습니다.

그런데... Programmer 형식에서는 아무것도 안받는다고 했는데...

이게 아다리(?) 죄송합니다... 이게 앞뒤가 안맞는거 아닙니까...


생성자에 이름과 월급이 명시가 되지 않은... 

즉, 부모 클래스와의 생성자 시그니처가 다르다면 컴파일러 입장에서는

기반 형식의 생성자를 호출할 때 이름과 월급을 뭐로 해야할지 모르게됩니다.


말이 길어졌군요... 

따라서 기본 생성자가 제공되지 않을 때에는 반드시... 반드시!!

파생 형식의 생성자에서 이니셜라이저를 통해 어떻게 만들 것인지에 대한 명시를 해줘야 

문제가 발생하지 않게 됩니다.


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
27
28
29
 
    
class Job
{
    int salary;
    string name;
public:
    Job(int _salary, string _name)
    {
        this->salary = _salary;
        this->name = _name;
    }
};
 
class Programmer
    :public Job
{
public:
    Programmer(int _salary, string _name) :Job(_salary, _name)
    {
 
    }
};
void main()
{
    Programmer *sap = new Programmer(5000"삽잡이");
}
 
 
cs


이렇게... 부모 클래스의 생성자에 넣을 것들을 이니셜라이저를 통해... 사용하게됩니다.



여담으로 말하자면...

뭐... 이니셜라이저로 초기화하는게 빨라요? 왜 이런거 만들었어요 라는 생각이 드시는 분이

만에하나 혹시라도 계신다면...


C++을 만드신 분에게 여쭈어 보시길....ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ


그냥 그런 생각이 들더라구요 저는... 

이게 빠르려나 아니면 생성자 몸체 안에 초기화하는게 빠르려나....

멤버 변수는 또 이니셜라이저를 왜 사용가능하게 해서 이런 고민을 주나...

싶었는데... 다 부질없죠 ㅎㅎ 만든 사람 마음이지...


그리고 속도에 대한 얘기가 나와서 하는 말인데...

자문을 구해봤는데


OOP 즉, 객체 지향 프로그래밍 같은 경우에는

속도, 메모리 효율성을 높이기 위한 방법론이 아니죠...


전자에 비해 개발 공정에서 논리적인 모순에 의해 버그가 발생할 수 있는 것들을 

미연에 방지하기 위한 방법론에 가깝다고 봅니다...... 


OOP는 속도, 효율, 스피드를 추구하는 언어가 아닙니다...

신뢰성이죠...


아무튼... 그렇다구요 ....


그럼 여기까지!


이상 삽잡이였습니다!