Turbo-C
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
터보-C 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
Lua 게시판
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C/C++ 강좌/문서
[30] (01) Constructor in C++
남병철.레조 [lezo] 12199 읽음    2009-06-21 11:44

[목차]

(1) 생성자와 소멸자
(2) 생성자 활용
   1. 무인수 생성자
   2. 2인수 생성자
   3. 1인수 생성자
   4. 인스턴스 데이터로서의 배열
   5. 멤버 배열 초기화
   6. 복사 생성자
   7. const 객체
   8. 생성과 파괴











(1) 생성자와 소멸자

   * 생성자란? 객체가 생성될때 자동 실행되는 함수이다.
   * 소멸자란? 객체가 소멸될때 자동 실행되는 함수이다.


( 생성과 소멸 테스트 )
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

class Base
{

public:

    Base(){     // Constructor
        cout << "I am the constructor" << endl;
    }

    ~Base(){    // Destructor
        cout << "I am the destructor" << endl;
    }

};

//---------------------------------------------------------------------------

#pragma argsused
void main()
{
    cout << "Starting main() function" << endl << endl;

    Base *B1 = new Base();
    Base *B2 = new Base();
    Base *B3 = new Base();

    cout << endl;

    delete B1;
    delete B2;
    delete B3;

    cout << endl << "Ending main() function" << endl;

    getch();

}

//---------------------------------------------------------------------------

(결과)
Starting main() function

I am the constructor
I am the constructor
I am the constructor

I am the destructor
I am the destructor
I am the destructor

Ending main() function

//---------------------------------------------------------------------------
(2) 생성자 활용 1. 기본 생성자(무인수 생성자)? Class에 생성자를 명시적으로 선언하지 않았을 경우 컴파일러는 무인수 생성자를 자동으로 만든다. 이를 기본 생성자라 한다. 즉, 인수가 없는 생성자를 기본생성자라 한다. 어떠 종류의 생성자를 명시적으로 선언하고 나면 생성자를 조작할 책임은 프로그래머에게 있다. 2. 2인수 생성자? 인수를 가진 생성자는 다양한 종류로 사용이 가능하나 가장 일반적으로 Class의 초기화에 가장많이 사용된다. Class를 초기화 할때는 초기화 목록을 사용한다. 초기화 목록이란? 생성자의 이름 뒤에 콜론이 오고 그 뒤에 초기화할 변수들(둘 이상 이면 쉼표로 분리)이 나열된다. 목록의 각 변수를 초기화하는데 사용하는 값은 변수이름 뒤에 소괄호로 묶어서 넣는다. class Base { private: int top; int bottom; public: Base() : top(-1), bottom(-1) {} }; ( 2인수 생성자 )
//---------------------------------------------------------------------------
// 작성일 : 2001.08.14
// 제  목 : 생성자로 Class 초기화
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

class BaseTime
{

private:
    int hours;      // 0 ~ 23
    int minutes;    // 0 ~ 59

public:

    BaseTime() : hours( 0 ), minutes( 0 ) {                // 무인수 Constructor
        cout << "I am the constructor0" << endl;
    }

    BaseTime( int h, int m ) : hours( h ), minutes( m ) {  // 2 인수 Constructor
        cout << "I am the constructor2" << endl;
    }

    void Display(){
        cout << hours << ':' << minutes << endl;
    }

    ~BaseTime(){    // Destructor
        cout << "I am the destructor" << endl;
    }

};

//---------------------------------------------------------------------------

#pragma argsused
void main()
{
    cout << "Starting main() function" << endl << endl;

    BaseTime *B1 = new BaseTime();
    BaseTime *B2 = new BaseTime( 3, 20 );
    BaseTime *B3 = new BaseTime( 4, 30 );

    cout << endl;
    B1->Display();
    B2->Display();
    B3->Display();
    cout << endl;

    delete B1;
    delete B2;
    delete B3;

    cout << endl << "Ending main() function" << endl;

    getch();

}

//---------------------------------------------------------------------------

(결과)
Starting main() function

I am the constructor0
I am the constructor2
I am the constructor2

0:0
3:20
4:30

I am the destructor
I am the destructor
I am the destructor

Ending main() function

//---------------------------------------------------------------------------
3. 1인수 생성자 1인수 생성자는 객체 지향 프로그래밍에서 한 Class의 객체를 다른 Class의 것으로 변환할 때 많이사용한다. 즉, 변환 가능한 Class를 만들고자 할 경우 1인수 생성자를 주로 사용한다. 명심할 사항은, 변수가 어떤 데이터형이라고 말하는 것은 객체가 어떤 Class라고 말하는 것과 같다는 것이다. (1인수 생성자 호출되는 예) class ClassA { ClassA( int I ) // 1인수 생성자 { // Int 값을 ClassA 값으로 변화 } }; void main() { int IntNum = 23; // 값을 가진 정수 ClassA CA(IntNum); // ClassA 객체를 int로 초기화 ClassA CB = IntNum; // ClassA 객체를 int로 초기화 // 둘다 모두 1인수 생성자를 호출하는 초기화 방법이다. } *** 선언시의 '=' 연산자는 대입연산자가 아니라 소괄호 문법과 똑같다. Ex. ClassA CA = IntNum; // ClassA CA(IntNum);와 같음 일반 char* 문자열을 Class 문자열로 변환해서 조작하는 예를 들어보자. - char* "Greetings"는 s1이라는 xString으로 변환된다. - display시에 쓰레기값이 보이지 않기위해 무인수 생성자로 초기화해 주는 것은 중요하다. *** 주의 1인수 생성자 str 인스턴스 데이터는 초기화할 수 없다. xString( char s[] ) : str( s[] ) {} --> 불가능(배열이므로) 배열을 다루는 것이므로, strcpy() 라이브러리 함수등을 사용하여 원소 하나하나를 복사해야한다. xString( char s[] ){ strcpy( str, s ); } ( 1인수 생성자 )
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.15
// 제  목 : 1인수 생성자
// 내  용 : 일반 char* 문자열을 문자열 Class로 변환해서 조작
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

const int MAX = 80;

class xString
{
private:
    char str[MAX];
public: // 생성자
    xString(){
        strcpy( str, "" );      // NULL 문자열로 초기화( 모든 값에 NULL 대입 )
    }

    xString( char s[] ){
        strcpy( str, s );       // 인수 문자열로 초기화
    }

public: // 멤버 함수
    void input(){
        cin.get( str, MAX );
    }

    void display(){
        cout << str;
    }

    void append( xString xs ){  // 인수 문자열을 덧붙임
        if( strlen( str ) + strlen( xs.str ) < MAX - 1 )
            strcat( str, xs.str );
        else
            cout << "\nError : xString too long" << endl;
    }
};

//---------------------------------------------------------------------------

#pragma argsused
void main()
{

    xString s2, s3;
    xString s1( "Greetings, " );

    cout << "Enter your name : ";
    s2.input();

    s1.append( s2 );
    s3 = s1;                        // 객체 간 대입
    s3.display();

    getch();

}

//---------------------------------------------------------------------------

(결과)
Enter your name : Lezo
Greetings, Lezo

//---------------------------------------------------------------------------
4. 인스턴스 데이터로서의 배열 class Stack { private: const int size = 20; int st[size]; int top; public: Stack() : top(100) {} }; 위와같이 Class 멤버변수를 초기화할 수 없다.(const int size = 20) 인스턴스 데이터의 초기화에 대해 알아야할 사항. * 인스턴스 변수는 각 객체에만 적용되기 때문에 서로 다른 객체의 같은 변수는 서로 다른 값을 가질 수 있다. 그러므로 인스턴스 데이터는 객체가 만들어 질 때에만 초기화될 수 있다. const 인스턴스 데어터는 생성자의 초기화 목록에서 초기화 해준다. class Stack { private: const int size; int st[size]; int top; public: Stack() : top(100), size(200) {} }; 이제 const int size의 초기화는 해결되었다. 문제는 int st[size]의 적용여부이다. 결론부터 말하자면 적용되지 않는다. 분명 아래와같은 오류메시지를 낼것이다. "Constant expression required" 문제의 요점은 컴파일 시간에 배열의 크기가 전달되어야 하지만 위의 내용을보면 컴파일된후 프로그램 동작후 인스턴스 생성이 이루어져야 한다.(시간상 맞지않다.) 해결책으로 2가지 방법이 있다. (1) enum 파헤치기 enum { size = 20 } 위 식의 개념은 새 데이터형을 지정하는 것이 아니라 값에 이름을 주는 것뿐이다. class Stack { private: enum { size = 20 }; // 컴파일 시간에 설정됨 int st[size]; int top; public: Stack() : top(100) {} }; (2) static const 변수 enum 파헤치기가 우아하지 않으므로 ANSI/ISO C++ 연구 그룹은 언어에 static const 인스턴스 변수의 초기화라는 새 구성을 추가했다. 그러나 주의할 사항은 이 새 구성을 다룰 경우, 구식 컴파일러에서 오류 가 발생할 수 있다는 것이다. class Stack { private: static const int size = 20; // 정적 상수 int st[size]; int top; public: Stack() : top(100) {} }; *** 여기서 의문이 생기는가? size를 그냥 값으로 입력해도 문제없이 실행된다. 하지만 그냥 값으로 넣을경우 프로그램의 가독성(값 자체의 목적)이 떨어지게 된다. 별일 아니라고 생각할 수 있지만 여럿이서 소스를 공유하는 프로그램을 작성해 본 경험이 있는 분이라면 충분히 고려해야할 사항이다. 또 그러한 이유때문에 ANSI/ISO C++ 연구 그룹이 나선것이다. 5. 멤버 배열 초기화 *** 상황 배열은 모든 객체에 대해 같고, 각 객체에 그 복사본을 사용하지 않을때 아래와 같이 초기화할 수 있다. (정적 변수와 같은 경우로 정적 배열을 지정하면 Class객체가 처음 생성될때 한번 생성되어 뒤이어 생성되는 모든 객체에서 사용할 수 있다. 만일을 대비해서 const화 시킨것이다.) ( 1인수 생성자 )
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.15
// 제  목 : 멤버 배열 초기화
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

    class weekday
    {
    private:
        static const int DPW = 7;
        static const int MAX = 10;
        static const char day_name[DPW][MAX];   // 선언

    public:
        Display(){
            cout << day_name[0] << endl;
            cout << day_name[1] << endl;
            cout << day_name[2] << endl;
            cout << day_name[3] << endl;
            cout << day_name[4] << endl;
            cout << day_name[5] << endl;
            cout << day_name[6] << endl;
        }

    };

    const char weekday::day_name[DPW][MAX] =    // 정의
                { "Sunday", "Monday", "Tuesday",
                  "Wednesday", "Thursday",
                  "Friday", "Saturday"
                };

//---------------------------------------------------------------------------

#pragma argsused
void main()
{

    weekday Dir;
    weekday DDD;
    Dir.Display();
    DDD.Display();
    getch();

}

//---------------------------------------------------------------------------

(결과)
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

//---------------------------------------------------------------------------
6. 복사 생성자 객체를 만드는 또 하나의 방법으로 기존 객체의 복사본을 만드는 것이다. - 기본 복사 생성자 복사 생성자를 명시적으로 정의하지 않으면 컴파일러가 기본 복사 생성자를 만든다. 이 기본 복사 생성자는 한 객체의 모든 데이터를 또 다른 객체에 구성원별로 복사하는 일을 수행한다. - 복사 생성자의 특징 특별한 이유가 있다면 몰라도, 참조 인수는 언제나 const로 만들어야 한다. 복사 생성자는 여러가지 면에서 또 다른 1인수 생성자라 할 수 있다. = Class와 같은 이름을 가진다. = 값을 리턴하지 않는다. 하지만 차이점은, 인수는 언제나 구성원을 가지고 있는 Class의 것이다. - 복사 생성자의 인수는 다음을 유의해야 한다. * 인수는 반드시 참조로 전달해야 한다.(시스템 다운을 방지하기 위해) * 인수는 const를 사용하여 안전성을 보장해야한다.
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.18
// 제  목 : 복사 생성자
// 내  용 : 복사 생성자의 간단한 예를 보자
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

class omega
{
private:
    static const int size = 20;
    char name[size];
    static int total;
    int number;

public:
    omega( char str[] ) : number(++total){
        strncpy( name, str, size );
        cout << "\nI am the 1-arg constructor. I have "
             << "created object " << name << "-" << number;
    }

    omega( const omega& om ){
        strncpy( name, om.name, size );
        number = ++total;
        cout << "\nI am the copy constructor. I have "
             << "created object " << name << "-" << number;
    }
};

int omega::total = 0;

//---------------------------------------------------------------------------

#pragma argsused
void main()
{

    omega om1("Harriet");

    omega om2=om1;
    omega om3(om1);

    getch();

}

//---------------------------------------------------------------------------

(결과)
I am the 1-arg constructor. I have created object Harriet-1
I am the copy constructor. I have created object Harriet-2
I am the copy constructor. I have created object Harriet-3

//---------------------------------------------------------------------------
7. const 객체 const 객체에서 const가 아닌 일반 구성원 함수를 사용하게 되면 컴파일러로 부터 아래와 같은 경고를 받을 것이다. Non-const function airtime::display() called for const object const 함수란 무엇인가? - const 함수 객체를 바꾸지 않도록 보장하는 구성원 함수이다.(this 포인터의 내용을 변경할 수 없다) - const 객체를 생성할때 유의할 사항 * 객체를 const로 생성할 경우에는 const 구성원 함수만을 사용해야 컴파일러의 경고를 받지 않는다. * const 함수는 일반 객체 선언이나 const 객체 선언 모두에서 사용할 수 있다. 하지만 일반 함수는 일반 선언에서만 사용하고 const 객체를 사용할 때는 사용 하지 않는것이 바람직하다. * const 객체는 name mangling이 적용된다. * const 객체 내부에서는 구성원 함수를 수정할 수 없다.(수정시 ERROR발생) - const 객체 정리. const 객체에는 일반 함수와 const 함수 모두를 사용할 수 있다 하지만 일반 함수를 사용하게 되면 컴파일러 warning을 받게되며 만일 그 함수가 const 객체의 데이타를 수정하려하면 컴파일 error를 출력한다. 물론 const 함수는 this 객체를 수정할 수 없다. 또한 const 함수는 name mangling이 적용되므로 함수명이 같더라도 const 함수이고 아니고의 구별이 가능하다 물론 const 객체에서 사용하는 함수는 const 함수가 적용되고 일반 함수에서 사용하는 함수는 일반 함수가 적용되게된다. 특히 const 객체를 사용했을경우는 일반 객체를 사용해서 구성원 함수를 수정하지 않으려고 노력하는 것보다 편리하게 프로그램할 수 있다. 객체가 생성될때 사용되는 생성자는 객체의 데이터를 바꾸므로 생성자는 const가 아니다. const 생성자를 지정하면 다음과 같은 오류 메시지가 나타난다. ( Constructor cannot be declared 'const' or 'volatile' )
//---------------------------------------------------------------------------
// 작성일 : 2001.08.19
// 제  목 : const 객체
// 내  용 : const 객체와 일반 객체와의 차이점(구성원 함수 수정시의 차이점 발생)
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include 
#pragma hdrstop

//---------------------------------------------------------------------------

class airtime
{
private:
    int hours;
    int minutes;

public:
    airtime() : hours( 0 ), minutes( 0 ) {}

    airtime( int h, int m ) : hours( h ), minutes( m ) {}

    void display() const;
    void get();
};

void airtime::display() const
{
    cout << hours << ':' << minutes;
}

void airtime::get()
{
    char dummy;
    cout << "\nEnter time (format 12:59) : ";
    cin >> hours >> dummy >> minutes;
}

//---------------------------------------------------------------------------

#pragma argsused
void main()
{

    const airtime noon( 12, 0 );

    cout << "\nnoon = ";
    noon.display();

    airtime at1;
    at1.get();
    cout << "at1 = ";
    at1.display();

    getch();

}

//---------------------------------------------------------------------------

(결과)
noon = 12:0
Enter time (format 12:59) : 11:11
at1 = 11:11

//---------------------------------------------------------------------------
8. 생성과 파괴 지금까지는 생성자에 대해서만 다루었지만 여기서는 생성자에 대한 복습 및 생성 자와 파괴자의 관계를 시각적인 예를 통해서 확인해 보겠다
//---------------------------------------------------------------------------]
// 작성일 : 2001.08.19
// 제  목 : 생성자와 파괴자
// 내  용 : 생성자와 파괴자의 발생을 시각적으로 보자
// 작성자 : 남병철
//---------------------------------------------------------------------------

#include 
#include          // strncpy()를 위해
#include           // getch()를 위해
#pragma hdrstop

//---------------------------------------------------------------------------

class omega
{
private:
    static const int size = 20;     // 배열의 크기
    char obname[size];              // obname 문자열의 배열
    static int total_now;           // 지금 존재하는 객체 수
    static int total_ever;          // 이 객체의 일련 번호
    int snumber;

public:
    // 1인수 생성자
    omega( char str[] ) : snumber( ++total_ever ) {
        strncpy( obname, str, size );
        cout << "\n    1-arg constructor creating "
             << obname << "-" << snumber
             << ". Total = " << ++total_now;
    }

    // 복사 생성자
    omega( const omega& om ) : snumber( ++total_ever ) {
        strncpy( obname, om.obname, size );
        cout << "\n    Copy constructor creating "
             << obname << "-" << snumber
             << ". Total = " << ++total_now;
    }

    // 파괴자
    ~omega() {
        cout << "\n    Destructor destroying    "
             << obname << "-" << snumber
             << ". Total = " << --total_now;
    }
};

int omega::total_now = 0;       // 정적 구성원 멤버 초기화
int omega::total_ever = 0;      // 상동

omega om0("Adam");              // 외부 객체

//---------------------------------------------------------------------------

#pragma argsused
void main()
{

    cout << "\nmain() starting";
    void func( omega );                     // 선언문

    omega om1( "Jane" );                    // 1인수 생성자를 사용
    omega om2( "Paul" );
    omega om3( om2 );                       // 복사 생성자를 사용
    cout << "\nmain() calling func( om1 )";
    func( om1 );
    cout << "\nmain() calling func( om2 )";
    func( om2 );
    cout << "\nmain() terminating";

    getch();

}

//---------------------------------------------------------------------------

void func( omega og )                       // 값으로 전달된 인수
{
    cout << "\nfunc() starting";
    omega om4( "Mike" );                    // 객체는 func()에 대해 지역임
    cout << "\nfunc() terminating";
}

//---------------------------------------------------------------------------

    1-arg constructor creating Adam-1. Total = 1
main() starting
    1-arg constructor creating Jane-2. Total = 2
    1-arg constructor creating Paul-3. Total = 3
    Copy constructor creating Paul-4. Total = 4
main() calling func( om1 )
    Copy constructor creating Jane-5. Total = 5
func() starting
    1-arg constructor creating Mike-6. Total = 6
func() terminating
    Copy constructor creating Mike-7. Total = 7
    Destructor destroying    Mike-6. Total = 6
    Destructor destroying    Jane-5. Total = 5
    Destructor destroying    Mike-7. Total = 4
main() calling func( om2 )
    Copy constructor creating Paul-8. Total = 5
func() starting
    1-arg constructor creating Mike-9. Total = 6
func() terminating
    Copy constructor creating Mike-10. Total = 7
    Destructor destroying    Mike-9. Total = 6
    Destructor destroying    Paul-8. Total = 5
    Destructor destroying    Mike-10. Total = 4
main() terminating
    Destructor destroying    Paul-4. Total = 3
    Destructor destroying    Paul-3. Total = 2
    Destructor destroying    Jane-2. Total = 1
    Destructor destroying    Adam-1. Total = 0

//---------------------------------------------------------------------------

+ -

관련 글 리스트
30 (01) Constructor in C++ 남병철.레조 12199 2009/06/21
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.