'클래스'에 해당되는 글 2건

Programming/C C++
함수 템플릿처럼 전체적인 구조는 갖지만 데이터형에 따라 다른 취급을 하는 각각의 클래스를 생성할 수 있습니다.
template <typename T>
class Base
{
public:
   void set_value(T i);
   T get_value();

private:
   T j;
};
클래스에 템플릿을 적용하려면 클래스 머리위에 template 키워드를 사용해야 합니다. 또한 <>안에 typename을 지정하여 뒤의 식별자가 데이터형임을 나타내도록 합니다.

식별자 T는 클래스 본문에서 필요한 멤버에 붙여 사용합니다. 현재 Base클래스는 T형 변수 j와 이 변수에 값을 저장하기 위한 set_value()함수 그리고 저장된 값을 불러오는 get_value()함수가 선언되어 있습니다.
template <typename T>
void Base<T>::set_value(T i)
{
   j = i;
}

template <typename T>
T Base<T>::get_value()
{
   return j;
}
클래스 멤버함수를 정의하는 부분에서도 typename 식별자가 필요한 경우 똑같이 템플릿을 적용해야 하며 특히 클래스명과 연결할때는 Base<T>처럼 데이터형을 의미하는 식별자까지 완벽하게 붙여야 하니다.
Base<int> b;
b.set_value(100);
cout << b.get_value() << endl;

Base<char> b2;
b2.set_value(65);
cout << b2.get_value() << endl;
템플릿이 적용된 클래스의 객체를 생성하는 경우 또한 클래스명에 <데이터형>을 붙여 어떤 데이터형의 값을 전달할 것이지 명확하게 지정해야 합니다.


클래스 템플릿은 필요한 경우 특정 데이터형의 값을 추가적으로 전달할 수도 있습니다.
template <typename T, int x = 10>
class Base
{
public:
   void set_value(int idx, T i);
   T get_value(int idx);

private:
   T j[x];
};
클래스 템플릿에 x 값을 추가하였는데 이 x는 int형으로서 10으로 초기화 됩니다. 그러나 클래스 내부에서는 x자체가 사용되는 것이 아니고 x에 대입된 10으로 값이 치환(j[x] == x[10]) 되어집니다.

위 예제에서는 x에 지정된 값만큼의 j배열이 생성될 것입니다.
template <typename T, int x>
void Base<T, x>::set_value(int idx, T i)
{
   j[idx] = i;
}

template <typename T, int x>
T Base<T, x>::get_value(int idx)
{
   return j[idx];
}
물론 멤버함수를 정의하는 경우에도 클래스의 템플릿과 일치시켜야 합니다. 다만 클래스에서처럼 값을 정해두지는 않습니다.

위 멤버함수는 인덱스를 지정하여 j배열에 있는 인덱스에 값을 설정하거나 가져올 수 있도록 하였습니다.

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

[Visual C++] 전처리문(Preprocessor)  (0) 2012.10.24
[Visual C++] 추상클래스  (0) 2012.10.19
[Visual C++] 클래스 템플릿  (0) 2012.10.18
[Visual C++] 업/다운 캐스팅  (0) 2012.10.17
[Visual C++] 프렌드 클래스  (0) 2012.10.16
[Visual C++] 배열  (3) 2012.10.10
0 0
Programming/C C++
 클래스와 개체

클래스는 int나 float와 같이 또 하나의 자료형으로 취급될 수 있습니다. 다만 그 자료형을 개발자가 만든다는 것에 차이가 존재합니다. 따라서 클래스는
int a;
처럼 메모리상에 위치할 실체인 객체형의 인스턴스를 생성해서 사용해야 하고 이때 생성된 인스턴스가 바로 개체에 해당하는 것입니다. 즉, 클래스를 자료형으로 하고 그에 맞는 변수(객체)를 생성하는 것과 같습니다.

 클래스 선언

C++에서 클래스는 다음의 형식으로 작성됩니다.
class MyClass
{
    멤버 변수;
    멤버 함수;
};

함수와는 다르게 클래스는 끝에 ; 문자가 붙는다는 점에 주의하십시오. MyClass는 클래스의 이름으로서 적절하게 개발자가 정하면 됩니다.

클래스 안에 여러 변수와 함수를 구현하여 실제 클래스를 정의하며 클래스 안에 포함된 변수나 함수를 클래스의 멤버라고 합니다. 다음은 클래스 작성의 예입니다.

class MyClass
{
    int d;

    void day(int a)
    {
        d = a;
    }
};
 접근제한자

클래스의 멤버에는 외부에서 접근이 가능한지 여부를 정할 수 있는 제한자가 존재합니다. 제한자로는 public, private, protected 세가지가 있는데 멤버에 특정한 제한자를 설정하지 않는 경우에는 기본적으로 private 형으로 취급됩니다.

public 접근자는 클래스 외부에서 해당 멤버에 대한 접근을 자유롭게 하며 private는 클래스 내부와 friend외에는 접근이 불가능하도록 합니다. protected는 private과 비슷하지만 추가적으로 클래스를 상속받은 파생클래스에서만 접근할 수 있습니다.
class MyClass
{
public:
    void day(int a)
    {
        d = a;
    }

private:
    int d;
};
위에서 처럼 public이 설정된 day 함수는 외부에서 접근이 가능하지만 private이 지정된 멤버변수 d에는 외부에서 접근할 수 없습니다.
MyClass myclass;
myclass.day(30);
 인라인 멤버함수 정의

위의 모든 예제에서는 멤버함수를 정의할때 클래스 내부에서 멤버함수를 정의하였습니다.

class MyClass
{
public:
    void day(int a)
    {
         d = a;
    }

private:
    int d;
};
클래스를 정의하는 방법은 위와 같은 방법 말고도 클래스 내부에서 멤버함수를 선언하고 외부에서 해당 함수를 정의하는 방법이 있습니다.
class MyClass
{
public:
    void day(int a);

private:
    int d;
};

void MyClass::day(int a)
{
    d = a;
}
클래스 내부에서는 day함수에 대한 선언만 되어 있고 외부에서 day함수가 구체적으로 어떠한 처리를 하는지 정의되어 있습니다.

멤버함수 정의부분에서는 MyClass라는 클래스 이름으로 :: 라는 범위지정자를 통해 day함수가 MyClass클래스 소속의 멤버함수라는 것을 알려주고 있습니다. 이렇게 하지 않으면 외부에서 정의된 멤버함수가 어떤 클래스에 소속되어야 하는지에 대한 모호한 문제가 생길 수 있습니다.

참고로 정의 정의 부분에는 클래스 내부에 있는 변수 d에 값을 대입하도록 되어 있는데 변수 사용시 클래스 내부가 아닌 전역적으로 선언된 변수라면 '::d' 처럼 지정하여 해당 변수가 전역변수임을 알려야 합니다.

컴파일러는 위와 같이 선언되고 정의된 함수에 대해서는 해당 함수호출부분이 발견되면 호출 코드를 아예 함수실행코드로 치환해 버립니다. 이러한 처리 방법은 함수호출에 대한 오버헤드를 감소함으로서 프로그램의 수행속도를 빠르게 하지만 프로그램자체의 크기는 커지는 단점이 있습니다.

통상 프로그램의 크기가 커지면 프로그램을 메모리에 로드하고 실행시키기 까지 시간이 더 오래 걸릴 수 있습니다.

 정적멤버

일반적인 클래스멤버는 클래스에 대한 인스턴스 생성시에 비로소 메모리에 올라오고 인스턴스 레벨에서 해당 멤버에 접근이 가능해지게 됩니다.

반면 정적멤버는 프로그램 실행시에 처음부터 메모리에 위치하게 되고 인스턴스 생성이 필요 없이 바로 클래스 레벨에서 접근이 가능하며 프로그램이 종료되어야 메모리에서 해제되는 특징이 있습니다.

int d;

class MyClass
{
public:
    static void day(int a)
    {
         ::d = a;
    }

private:
    static int i;
};
어떤 멤버함수를 정적멤버화 하려면 멤버선언 앞에 static라고만 붙여주면 됩니다. 그러면 클래스 레벨에서 해당 멤버로 접근할 수 있습니다.
//MyClass myclass; -> 인스턴스 생성이 필요없음
 MyClass::day(30);
그런데 멤버함수가 아닌 멤버변수가 정적멤버로 지정된 경우에는 인스턴스를 생성한다 해도 메모리에 로드되지 않습니다. 정적멤버와 객체는 다른 메모리 공간을 사용하기 때문인데 다음과 같이 클래스 외부에서 정적멤버변수에 대한 초기화를 수행해 줘야 합니다.
int MyClass::i = 0;
위 초기화는 특정 함수내부나 클래스 내부가 아닌 변수 d 처럼 외부에 나와 있어야 합니다.

 생성자와 소멸자

클래스의 생성자는 클래스의 개체가 생성될때 호출되며 반대로 소멸자는 개체가 소멸될때 호출되는 함수입니다.

클래스의 생성자와 소멸자는 다음과 같은 형식으로 작성됩니다.
class 클래스명
{
    클래스명(); // 생성자
    ~클래스명() // 소멸자
};

클래스 이름과 같은 함수명을 작성하면 그 함수가 생성자가 되며 앞에 ~문자를 추가하면 소멸자가 됩니다. 다만 생성자와 소멸자는 클래스를 생성할때마다 붙여줄 필요가 없는데 이런 경우 컴파일러가 기본 생성자와 소멸자를 알아서 붙여주게 됩니다.

class MyClass
{
    int i;

    MyClass(int a)
    {
         i = a;
    }
};

위 예제에서 처럼 생성자를 오버로딩하면 클래스 내부의 멤버변수 값을 초기화 하거나 그외 기타 필요한 작업을 수행할 수 있습니다.

여기서 멤버변수는 생성자 이외에도 초기화 라는 구현을 통해 멤버변수를 초기화 할 수 있습니다.

class MyClass
{
    int i;

    int x;

    MyClass(int a);
};

MyClass::MyClass(int a) : x(10)
{
    i = a;
}
위 예제에서는 함수정의 부분에서 : x(10) 을 통해 멤버변수 x에 대한 초기값을 설정해 주고 있습니다. 직접 변수에 값을 주는 방법 이외에도 매개변수로 초기값을 설정해 줄 수도 있습니다.
MyClass::MyClass(int a) : x(a) //매개변수 a의 값으로 멤버변수 x를 초기화
{
    i = a;
}
 상속

특정 클래스에 대한 상속을 구현하면 해당 클래스의 멤버함수와 멤버변수를 물려받을 수 있습니다. 이때 물려받는 자식클래스를 파생클래스(Derived Class), 물려주는 부모클래스를 기반클래스(Base Class)라고 합니다.
class BaseClass
{
};
위와 같은 형태의 기반클래스가 있다고 가정할 경우 이를 상속받는 파생클래스를 구현하려면 다음과 같이 작성합니다.
class DerivedClass : [접근한정자] BaseClass
{
};
이 예제에서 기반클래스는 BaseClass가 되며 파생클래스는 DerivedClass가 됩니다. [접근한정자] 부분은 클래스를 상속받을때 어떤 유형으로 멤버를 상속받을지를 나타내는 부분으로서 아래 중 하나를 사용할 수 있습니다.

 public  기반클래스의 public와 protected 멤버를 그대로 상속받습니다.
 protected  기반클래스의 public와 protected를 protected로 상속받습니다.
 private  기반클래스의 public와 protected를 private로 상속받습니다.

만약 파생클래스의 객체가 생성되는 경우 기반클래스의 생성자가 우선적으로 호출되고 그 다음 파생클래스의 객체를 호출하게 됩니다. 물론 반대의 경우에는 파생클래스의 소멸자를 우선으로 호출하고 뒤이어 기반클래스의 소멸자를 호출합니다.

class BaseClass
{
public:
 BaseClass();
 int mValue;
};

BaseClass::BaseClass() : mValue(10)
{
 cout << "1 - value : " << mValue << endl;
}

class DerivedClass : public BaseClass
{
public:
 void valuePrint();
};

void DerivedClass::valuePrint()
{
 cout << "2 - value : " << mValue << endl;
}

int main()
{
 DerivedClass dc;
 dc.valuePrint();

 return 1;
}
위 코드는 클래스의 상속과 생성자 호출 순서를 확인해 보는 예제입니다. 한가지 더 알려드릴건 파생클래스는 다음과 같이 여러 기반클래스로 부터 상속받을 수 있는데
class DerivedClass : public BaseClass1, public BaseClass2, ....
{
public:
 void valuePrint();
};
이때의 생성자 호출 순서는 기반클래스를 작성한 순서(public BaseClass1, public BaseClass2, ....
)에 따르게 되며 반대로 종료될때는 맨뒤에 지정된 클래스(BaseClass2)의 소멸자부터 순서대로 호출됩니다.

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

[Visual C++] 배열  (3) 2012.10.10
[Visual C++] 복사생성자  (0) 2012.10.09
[Visual C++] 클래스(Class) 의 기본  (5) 2012.10.08
[Visual C++] 연산자 오버로딩(Operator Overloading)  (0) 2012.10.05
[Visual C++] 포인터와 참조  (2) 2012.07.12
[Visual C++] typedef  (0) 2012.07.06
5 0
1
블로그 이미지

클리엘