'Programming/C C++'에 해당되는 글 73건

Programming/C C++
기반클래스 하나를 두개의 파생클래스가 똑같이 상속받고 이 두개의 파생클래스를 다시 하나의 또 다른 파생클래스가 상속받는 경우
class Base

class Deri1:public Base
class Deri2:public Base

class Deri_end:public Deri1, public Deri2

기반클래스(Base)는 이를 상속받는 파생클래스(Deri1, Deri2)마다 하나씩 개별적으로 생성됩니다.


따라서 위와 같은 상속의 형태를 갖게 되는데 이를


위와 같은 형태로 바꾸고자 한다면 가상상속을 구현해야 합니다.
class Deri1:public virtual Base
{
};

class Deri2:public virtual Base
{
};
가상상속은 간단한데 파생클래스에서 기반클래스를 상속받을때 virtual 키워드를 포함하기만 하면 됩니다. 실제로 기반클래스에 생성자와 소멸자를 추가하고
class Base
{
public:
   Base::Base();
   Base::~Base();
};

Base::Base()
{
   cout << "기반 클래스 생성자" << endl;
}

Base::~Base()
{
   cout << "기반 클래스 소멸자" << endl;
}
프로그램을 빌드하여 실행시켜 보면
Deri_end* de = new Deri_end();

delete de;
다음과 같이


기반클래스의 생성자와 소멸자가 한번씩만 호출되었음을 알 수 있습니다. 하나의 기반클래스를 두개의 파생클래서에서 상속받았음에도 불구하고 각각의 파생클래스에서 기반클래스 하나만을 공유하고 있기 때문입니다.
0 0
Programming/C C++
가장 흔히 사용되는 예외처리 대표 구문으로 try ~ catch 문이 있습니다.
try
{
   int x = 0;
   int y = 0;

   if (x == 0)
    throw 10;

   int i = x/y;
}
catch(int e)
{
   cout << "오류코드 " << e << "번 발생" << endl;
}
try 문 안에서 throw구문으로 특정 데이터형의 값을 전달하면 catch 문의 인자(int e)에서 이 값을 받아 예외를 처리하게 됩니다. 따라서 throw에서 던지는 값의 데이터형과 catch에서 받는 데이터형은 항상 일치해야 합니다.

만약 여러 데이터형의 오류값을 throw하는 경우라면 다음처럼 catch를 그만큼 중복해서 만들 수 있습니다.
catch(int e)
{
   cout << "오류코드 " << e << "번 발생" << endl;
}
catch(char e)
{
   //
}
그런데 try안에서 던지는 throw는 반드시 try안에 있을 필요는 없습니다. 대신 최초 throw가 시작되면 항상 그 끝에는 catch가 throw값을 받을 수 있는 구조로 되어 있어야 합니다.
int plus(int i, int j)
{
   throw 0;
   return i + j;
}

int main()
{
   try
   {
      int x = plus(100, 200);
   }
   catch(int e)
   {
      cout << "오류코드 " << e << "번 발생" << endl;
   }

    return 0;
}

try 안에서 plus를 호출하고 plus안에서 throw 발생시킵니다. throw가 처음 plus에서 예외를 받아줄 catch를 찾고 그 안에서 catch가 존재하지 않으면 plus를 빠져나와 다시 catch를 찾게 됩니다. plsu함수를 빠져나오면 try안이고 이 안에 catch가 존재하게 되므로 결국 throw은 try안의 catch를 찾아가게 되는 것입니다.


예외처리를 좀더 구조화 하려면 예외처리 자체를 클래스로 만들어 사용합니다.
class exc
{
public:
   exc(int number, const char* msg) : e_number(number)
   {
      strcpy_s(e_msg, msg);
   }

   int get_e_number();
   const char* get_e_msg();

private:
   int e_number;
   char e_msg[255];
};

int exc::get_e_number()
{
   return e_number;
}

const char* exc::get_e_msg()
{
   return e_msg;
}
exc 라는 클래스는 오류코드와 그 내용을 받는 예외전용 클래스로서 클래스를 생성할때 생성자로부터 오류 번호와 오류 내용을 받으며 이를 다시 가져올 수 있는 멤버함수를 포함하고 있습니다.
try
{
   throw exc(100, "에러발생");
}
catch(exc& e)
{
   cout << "오류코드 " << e.get_e_number() << "번 " << e.get_e_msg() << endl;
}
예외 발생시 exc 클래스에 처리하고자 하는 오류번호와 내용을 전달합니다. catch에서 어떠한 자료형이든 받을 수 있다는 점을 이용한 것인데 자세히 보시면 &를 붙여 참조를 통해 try에서 생성한 객체에 접근하고 있음을 알 수 있습니다.

try안에서 생성한 객체는 try를 벗어나면(catch에 도달하면) 객체가 소멸되므로 C++에서는 객체를 throw하는 경우 객체의 복사본을 생성하게 됩니다. 따라서 catch에서는 이 복사본의 참조를 받아오도록 하는 것이죠. 물론 &를 제거하고 값자체로서 받아올 수 있으나 이렇게 되면 복사본을 다시 복사해버리는 성능상의 손실을 발생시킬 수 있으므로 권장하지 않습니다.
0 0
Programming/C C++
 오버라이딩(Overriding)

기반클래스의 멤버함수를 파생클래스에서 재정의하는 것을 의미합니다.
class Base
{
public:
   virtual int cals(int a, int b);
};

int Base::cals(int a, int b)
{
   return a + b;
}
Base 클래스에서 cals함수를 정의하고 파라메터로 받은 a, b두개의 정수값을 더하여 그 결과를 반환하도록 하였습니다. 이때 clas함수는 virtual 키워드를 사용하여 선언하였는데 이는 파생클래스에서 해당 함수의 기능을 다시 정의할 수 있도록 허용하기 위함입니다.
class Deri:public Base
{
public:
   int cals(int a, int b);
};

int Deri::cals(int a, int b)
{
   return a - b;
}
Base 클래스를 상속받은 Deri 클래스는 Base에서 정의된 cals를 함수이름과 파라메터 형식을 일치시켜 a와 b두개의 값을 서로 감산하도록 새로 정의하였습니다.
Base b;
int rb = b.cals(10, 20);

Deri d;
int rd = d.cals(200, 100);

cout << rb << endl;
cout << rd << endl;

기반 클래스에서 함수를 정의할때 너무 많은 함수를 virtual 키워드로 선언하지 않도록 주의해야 합니다. 가상함수는 가상함수테이블이라는 저장공간에 가상함수목록을 만들어 넣어두고 프로그램 실행중 가상함수가 호출될때 가상함수테이블에서 현재 실행해야될 함수를 찾아 실행하게 됩니다.

당연히 가상함수테이블을 기억할 저장공간이 따로 필요하게 되고 가상함수테이블에서 원하는 함수를 찾는 작업도 선행되어야 합니다. 따라서 일반 함수를 실행할때 보다 성능적인 차이가 발생할 수 밖에 없습니다.

 다형클래스

가상함수를 가지고 있는 클래스를 다형클래스라고 하는데 이 다형클래스의 함수포인터를 이용해 경우에 따라 다른 동작할 하는 파생클래스의 객체를 가리켜 원하는 멤버함수를 호출할 수 있도록 합니다.

다형 클래스의 특징을 알아보기 위해 위 예제에서의 Base클래스와 이를 상속하는 Deri 클래스에 더하여 * 처리를 하는 다른 Deri클래스를 하나더 생성해 보겠습니다.
class Deri2:public Base
{
public:
   int cals(int a, int b);
};

int Deri2::cals(int a, int b)
{
   return a * b;
}

함수 오버라이딩의 예제에서는 Base b;와 Deri d;처럼 각각의 클래스에 해당하는 객체를 생성하였지만 사실 그렇게 할 필요가 없이 Base 객체의 포인터를 통해 힙에 할당된 파생클래스의 주소를 가리켜 해당 멤버함수를 호출하도록 하면 같은 결과를 얻을 수 있습니다.

Base* b;

b = new Deri();
int r = b->cals(10, 20);
cout << r << endl;

b = new Deri2();
r = b->cals(15, 25);
cout << r << endl;

오버라이딩부분에서 가상함수테이블에 대하여 설명을 드렸는데 이런 것처럼 컴파일 단계에서가 아닌 프로그램 실행중 함수가 호출될때 함수호출부분과 실행부분의 바인딩(연결)이 이루어 지는 것을 후기바인딩이라고 합니다.

다형클래스의 활용도 이 후기바인딩을 이용하는 것입니다.
0 0
Programming/C C++
두개의 값을 받아 단순히 더해주는 함수를 만들어야 한다고 가정해 보겠습니다.
int plus(int i, int j)
{
    return i + j;
}
그런데 만약 위 함수에서 i와 j의 데이터형이 달라져야 한다면 어떻게 될까요? 새로운 형의 데이터를 처리하는 함수를 만들어 오버라이딩하는 것도 방법이지만 이런 경우 함수 템플릿을 이용하면 좀더 효휼적으로 문제를 해결할 수 있습니다.
template <typename T>
T plus(T i, T j)
{
   return i + j;
}
위의 경우가 plus함수에 템플릿을 적용한 경우입니다. 템플릿은 template 키워드를 사용하며 <> 안에 typename으로 뒤의 식별자가 데이터형임을 지정합니다. 따라서 T가 데이터형이 되며 '어떠한' 데이터형의 매개변수를 받아 동작할지 그 틀을 잡게됩니다.

함수의 반환형과 매개변수도 모두 T 로 지정되어 있으므로 함수를 호출할때 실제 전달되는 매개변수의 데이터형에 따라가게 됩니다.
cout << plus(100, 200) << endl;
템플릿이 지정된 함수를 호출합니다. 100이나 200은 정수에 해당하므로 plus는 정수형 데이터에 맞게 덧셈을 수행하고 그 결과를 반환할 것입니다.
cout << plus<int>(100, 200) << endl;
특정 데이터형을 명시적으로 지정하여 함수를 호출할 수도 있습니다.
cout << plus(100, 2.3f) << endl;
이런 경우는 곤란합니다. T가 어떠한 데이터형으로 처리해야 할지 알 수 없기 때문입니다. 굳이 위와 같은처리를 하려면 <int>처럼 데이터형을 명시적으로 지정하여 2.3 에대한 매개변수 데이터를 int로 형변환이 되도록 하거나
template <typename T, typename T1>
T plus(T i, T1 j)
{
   return i + (int)j;
}
위와 같이 데이터형을 구분할 수 있도록 템플릿을 지정해야 합니다.

0 0
Programming/C C++
전처리란 소스코드를 컴파일하기전 소스코드에 특별한 처리를 행하는 것을 말합니다.

1. #include

소스에 헤더파일이나 기타 소스파일을 포함시킵니다. #include 를 사용하는 방법은 두가지가 있는데 하나는 <>를 사용하는 것으로 이 경우 해당 파일은 컴파일옵션에 미리 정해진 경로에서 파일을 찾게되며 ""는 현재 소스파일이 위치한 경로에서 지정한 파일을 찾게 됩니다. 이때 만약 파일을 찾지 못하면  없으면 <>와 같은 경로에서 파일을 찾게 됩니다.

2. #import

COM/ActiveX 개발시 자주 사용되는 것으로 형정보를 해당 라이브러리에서 가져오도록 합니다.

3. #using

.NET 관련 라이브러리를 참조합니다.

4. #if

다음 형식으로 사용되며
#if 조건
//내용
#elif 조건
//내용
#else
//내용
#endif
소스코드에 조건부(특정 플렛폼이나 언어등..) 컴파일을 지시합니다.

조건이 하나뿐인 경우 #elif 또는 #else는 생략할 수 있으며 여러줄의 내용을 포함하고 있더라도 일반적인 if문 처럼 {}로 묶을 필요가 없습니다.

5. #define

기본적으로 다음과 같이 상수를 정의하는데 사용됩니다.
#define MYVALUE 100
cout << MYVALUE << endl;
값으로는 100이나 200처럼 정수이외에 실수나 문자열형도 가능합니다.

#define는 상수를 정의하는 기능 이외에 함수자체를 정의하는 매크로 함수기능으로도 사용이 가능합니다.
#define PRINT(s) cout << s << endl
PRINT("hi!");

PRINT는 곧 위에 이은 cout 이하문과 동일하며 s는 매개변수부분에 해당합니다. 오해하지 말아야 할 부분은 PRINT자체인데 이것은 함수가 아닙니다. #define는 단순 전처리문에 불과하므로 PRINT는 컴파일전 cout 이하문으로 소스코드가 바뀌게 됩니다.

앞으로도 안내하는 모든 전처리문이 이와 같이 동작합니다. 소스코드를 바꿔주는 것일뿐 새로운 무엇인가를 만들어 주는 것이 아닙니다.

위에서 처럼 매개변수를 활용한 매크로함수를 구현하는 경우 #을 사용하면 전달되는 매개변수자체를 문자열로 취급할 수 있게 됩니다.

#define PRINT(s) cout << #s << endl

char* s = "hi!";
PRINT(s);

이 코드를 확인해 보면 문자 's'만이 출력되는데 전달되는 매개변수 s의 내용을 가져오는것이 아니라 #에 의해 변수자체를 그냥 값으로 취급해 버리기 때문입니다.

#을 ## 처럼 두개사용하면 매크로 정의된 내용에서 매개변수로 전달된 내용과 결합시키는 동작을 구현할 수도 있습니다.

#define type_declare(type, name) type my_##name

type_declare(int, value);

my_value = 100;
cout << my_value << endl;
type_declare는 두개의 매개변수를 받아 전처리 처리에 의해 type 이하 구문으로 바뀌게 됩니다. 이때 첫번째 매개변수는 type이며 int를 전달하면 type은 곧 int가 되고 name매개변수는 ##name으로 my_와 결합하여 새로운 식별자를 완성하게 됩니다.

단순한 상수를 정의하는 경우 const로도 처리할 수 있는데 #define와의 차이점은 const의 경우 데이터형을 엄격히 따지지만 #define는 데이터형을 별도로 따지지 않으므로 형검사면에서 #define이 좀더 취약한 면을 가지고 있다고 할 수 있습니다.

6. #undef

#define로 정의된 상수를 제거하는 역활을 합니다.
#define MYVALUE 100 //상수정의
cout << MYVALUE << endl; //출력
#undef MYVALUE //상수제거
cout << MYVALUE << endl; //오류 - 제거된 상수임
7. #ifdef / #ifndef

첫번째는 해당 식별자가 정의되어 있으면 true 이고 두번째는 식별자가 정의되어 있지 않으면 true를 수행하는 전처리문입니다.
#define MYVALUE 100

#ifdef MYVALUE
    cout << "상수정의됨" << endl;
#endif

#undef MYVALUE

#ifndef MYVALUE
   cout << "상수 정의되어 있지 않음" << endl;
#endif
0 0
Programming/C C++
추상클래스는 순수가상함수를 가진 클래스를 의미합니다. 기반 클래스로부터 상속받는 파생클래스에서는 반드시 기반클래스에 있는 순수가상함수를 재정의 하도록 강제합니다.
virtual int cals(int a, int b) = 0;
순수가상함수의 선언방식은 위와 같습니다. 가상함수에 =0을 붙이면 되는데 함수에 0을 대입하는것이 아닌 해당 함수를 순수가상함수로 만들겠다는 의미입니다.

클래스가 순수가상함수를 가지게 되면 해당클래스에서는 순수가상함수의 실행부분을 정의할 필요가 없고 클래스에 대한 객체를 생성할 수 없게 됩니다. 오로지 파생클래스에서 기반클래스에 있는 순수가상함수를 재정의하여 실행본체를 작성해야 하고 기반클래스만이 객체를 생성할 수 있게 됩니다.
class Base
{
public:
   virtual int cals(int a, int b) = 0;
};
기반클래스는 순수가상함수가 선언되면 해당 가상함수에 대한 실행부분을 정의하지 않습니다.
class Deri:public Base
{
public:
   int cals(int a, int b);
};

int Deri::cals(int a, int b)
{
   return a - b;
}
파생클래스에서 순수가상함수를 가진 클래스를 상속받으면 순수가상함수를 재정의하여 기능을 구현해야 합니다.
Deri* d = new Deri();
int r = d->cals(200, 100);
cout << r << endl;

0 0
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++
클래스 객체를 기반 클래스로 변환하는것을 업캐스팅, 파생클래스형으로 변환하는 것을 다운캐스팅이라고 합니다.

Base 클래스를 기반클래스, Deri 클래스를 기반클래스라고 가정했을 경우
Base* b = new Deri();
또는
Deri* d = new Deri();
Base* b = dynamic_cast<Base*>(d);
위와 같이 하는것이 방법이 업캐스팅이 됩니다. dynamic_cast는 캐스팅 함수로서 dynamic_cast<변환형>(대상객체); 형식으로 작성됩니다.

다운캐스팅은 dynamic_cast함수를 사용해야 합니다.
Base* b = new Base();
Deri* d = dynamic_cast<Deri*>(b);
캐스팅을 하기전에 dynamic_cast는 변환이 가능한지에 대한 확인작업을 먼저 하게 되는데 이를 생략하고 곧장 캐스팅을 수행하는 static_cast함수도 있습니다. 물론 static_cast 함수 사용에 대한 책임은 개발자에게 있을 것입니다.
Deri* d = static_cast<Deri*>(b);

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

[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
[Visual C++] 복사생성자  (0) 2012.10.09
0 0
Programming/C C++
클래스에서 멤버를 private 안에 두면 외부에서는 접근할 수 없습니다. 하지만 특정 클래스를 프렌드로 선언해두면 모든 멤버에 자유롭게 접근하도록 허용할 수 있습니다. 
class Base
{
   friend class deri;
private:
   int value;
};
Base 클래스에서 deri라는 이름의 클래스를 friend로 지정하였습니다. 그 하위에 value라는 변수를 private안에 두었는데 private 키워드의 규칙대로라면 외부에서는 value변수에 접근할 수 없을 것입니다.
class deri
{
public:
   void b_value();
};

void deri::b_value()
{
   Base b;
   b.value = 10;

   cout << b.value << endl;
}
deri 클래스는 Base 클래스의 value변수에 값을 지정하고 그 값을 학인하는 b_value() 함수를 가지고 있습니다. 이는 deri 클래스가 Base 클래스에서 friend로 선언되어 있기에 value 변수로의 접근이 가능한 것입니다.
deri d;
d.b_value();
deri 의 b_value()함수를 호출하여 결과를 확인합니다.

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

[Visual C++] 클래스 템플릿  (0) 2012.10.18
[Visual C++] 업/다운 캐스팅  (0) 2012.10.17
[Visual C++] 프렌드 클래스  (0) 2012.10.16
[Visual C++] 배열  (3) 2012.10.10
[Visual C++] 복사생성자  (0) 2012.10.09
[Visual C++] 클래스(Class) 의 기본  (5) 2012.10.08
0 0
Programming/C C++
배열은 아래와 같은 형식으로 작성됩니다.
데이터형 배열명[배열길이];
예를 들어 int형 변수 5개를 선언한다고 하면
int intarry[5];
위와 같이 하면 됩니다. 이렇게 배열을 선언하면 메모리상에 int에 해당하는 4byte공간이 연속적으로 할당되며 배열의 이름인 intarray는 메모리상에 할당된 공간의 시작주소를 갖게됩니다.

배열의 시작은 0부터 시작하므로 위의 배열은 0~4까지 5개의 배열을 가지게 되며 각각의 개별적인 배열에는 다음과 같은 방법으로 인덱스를 부여하면 됩니다.
intarray[0] = 10;
intarray[1] = 20;
배열은 선언과 초기화를 따로 하지 않고도 배열을 선언할때 {}를 사용하여 선언과 동시에 초기화할 수 있습니다.
int intarray[5] = {10, 20, 30, 40, 50};
지금까지의 배열은 1차원 배열입니다. 만일 이 배열을 2차원이나 3차원등의 배열로 만들고자 한다면 []를 사용하여 원하는 차원의 수만큼 연속적으로 지정해 주면 됩니다.
int intarry[5][3]; //2차원
int intarry[5][3][2]; //3차원
다차원 배열에 대한 접근은 1차원 배열과 같습니다. 예를 들어 위 2차원 배열의 경우
intarry[0][0] = 10;
intarry[0][1] = 10;
intarry[0][2] = 10;

intarry[1][0] = 20;
intarry[1][1] = 20;
intarry[1][2] = 20;

intarry[2][0] = 30;
intarry[2][1] = 30;
intarry[2][2] = 30;

intarry[3][0] = 40;
intarry[3][1] = 40;
intarry[3][2] = 40;

intarry[4][0] = 50;
intarry[4][1] = 50;
intarry[4][2] = 50;
이와 같은 배열의 접근이 가능합니다.

배열은 C언어 세계에서 문자열을 표현하는데 많이 사용되는 방법입니다. 문자열을 초기화 할때는 {}가 아닌(물론 가능하긴 합니다만) 아래와 간단한 방법을 주로 이용합니다.

char s[6] = "cliel";
문자열을 이루는 문자는 5자인데 배열을 6으로 한 이유는 문자열의 끝을 구분하는 특수문자인 \0 문자도 같이 할당되어야 하기 때문입니다.
 
이 방법은 일반적인 배열을 초기화 할때보다 간편하긴 하지만 문자열길이+\0 에 대한 값을 배열의 길이값으로 지정해야 한다는 불편함이 있으므로 C++에서는 다음과 같이 문자열을 초기화 하는 방법도 허용합니다.
char* s = "cliel";
포인터를 사용하는 경우에는 우선 cliel 문자열을 배열로서 메모리에 할당한 다음 첫번째 요소에 대한 주소값을 포인터 변수 s에 대입하는 방법으로 문자열을 다루게 됩니다.

포인터변수도 배열로서 다룰 수 있는데 다음은 그 방법을 보여주고 있습니다.
char* ss[3];

ss[0] = "aaaaa";
ss[1] = "bbbbb";
ss[2] = "ccccc";

for (int i = 0; i < 3; i++) {
   cout << ss[i] << endl;
}
포인터를 사용하는것은 문자열이나 정수와 같은 일반적인 데이터형도 가능합니다.
int i[3];

i[0] = 1;
i[1] = 2;
i[2] = 3;

int* ii[3] = { &i[0], &i[1], &i[2] };

for (int i = 0; i < 3; i++) {
   cout << "address : i[" << i << "] - " << &ii[i] << endl;
   cout << "value : i[" << i << "] - " << *ii[i] << endl;
}

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

[Visual C++] 업/다운 캐스팅  (0) 2012.10.17
[Visual C++] 프렌드 클래스  (0) 2012.10.16
[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
3 0
Programming/C C++
같은 클래스의 객체간 다음과 같은 구현을 하는 경우
Base b;

Base b2;
b2 = b;

//또는

Base b2(b);

원본객체가 갖고 있던 모든 멤버의 값을 대상 객체에 그대로 복사해 넣게 됩니다.

그런데 멤버중 주소값을 갖는 포인터가 있는 경우에는 포인터가 지시하고 있는 메모리의 데이터를 복사하는 것이 아니라 메모리의 주소값만을 복사하게 됩니다. 이렇게 되면 원본이나 복사본 객체에서 해당 메모리를 해제하거나 값을 변경하는 경우 다른 객체에도 똑같이 영향을 받게 되는 것이죠.

이러현 동작을 원하지 않는 경우에는 복사생성자를 직접 작성하여 포인터가 가진 메모리안의 값을 직접 복사해 오도록 구현해야 합니다.

class Base
{
public:
   int* value;
   Base(const Base& b);
   Base::Base();
   Base::~Base();
};

Base::Base(const Base& b)
{
   value = new int;
   *value = *(b.value);
}

Base::Base()
{
   value = new int;
}

Base::~Base()
{
   delete value;
}
Base에 복사생성자(const Base& b)를 직접 작성하여 매개변수로 넘어온 객체가 가진 value값을 가져오도록 하였습니다.
Base b;
*(b.value) = 30;

Base b2(b);
*(b2.value) = 50;

cout << *(b.value) << endl;
cout << *(b2.value) << endl;
Base에 대한 원본객체를 생성하고 value에 값을 할당한뒤 b2라는 또 다른 객체를 만들어 b의 멤버값을 복사하도록 하였습니다.

이때 value라는 포인터는 위에서 만든 복사생성자에 의해 주소가 아닌 값이 복사되도록 하였으므로 b와 b2가 가진 value값은 다르게 출력될 것입니다.

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

[Visual C++] 프렌드 클래스  (0) 2012.10.16
[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
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
Programming/C C++
+ 나 - 같은 연산자는 통상 정수같은 수치연산에 사용되는 것이 기본입니다.
int i = 10;
int j = 5;

cout << i - j << endl;
그러나 위와 같이 기본적으로 정의된 연산자의 기능을 클래스(Class)단위에서 재정의하여 사용할 수 있는데 이것을 연산자 오버로딩(Operator Overloading)이라고 합니다.
class number_2
{
public:
   int i;
   int j;

   number_2(int a, int b);
   number_2 operator + (number_2 n2);
};

위 number_2 클래스에는 +연산자에 대한 오버로딩이 구현되어 있는데 7행은 클래스생성자이고 8행이 연산자 오버로딩 부분입니다.

연산자 오버로딩은 위 예제처럼 '클래스 operator + (데이터형)' 형식으로 선언됩니다.

number_2 number_2::operator + (number_2 n2)
{
   i += n2.i;
   j += n2.j;

 return number_2(i, j);
}
연산자 오버로딩을 선언하였으면 실제 클래스에 대해 연산자가 어떤형태로 기능을 수행할 것인지에 대해 위 예제처럼 그 기능이 정의되어 있어야 합니다.

예제는 데이터형으로 전달받은 클래스의 객체에서 i와 j변수의 값을 가져와 자신이 가지고 있는 i와 j변수의 값을 더하게 됩니다. 그런 후 6행에 보시는 것처럼 해당 클래스에 대한 생성자를 호출하고 그 객체를 반환하도록 하였습니다.
number_2::number_2(int a, int b):i(a), j(b)
{
 //
}
number_2의 생성자입니다. 생성자 호출시 전달받은 a와 b의 값을 자신의 i와 j변수에 순서대로 대입합니다.
number_2 n2_1(10, 20);
number_2 n2_2(100, 200);

number_2 n2_3 = n2_1 + n2_2;

cout << n2_3.i << " - " << n2_3.j << endl;
1과 2행에서 number_2의 객체를 각각 생성하고 4행에서 생성된 클래스에 대해 + 연산을 수행하고 있습니다. + 연산자는 객체가 가지고 있는 i와 j를 더하여 새로운 클래스의 객체를 반환해야 한다는 것을 클래스내부의 연산자 오버로딩 부분을 통하여 잘 알고 있는 상황이므로 원하는 결과값을 볼 수 있을 것입니다.

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

[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
[Visual C++] 구조체  (0) 2012.07.05
0 0
Programming/C C++
 포인터

1) 포인터 기본

포인터라는 것은 특정 값을 가지는 일반적인 변수와는 다르게 메모리 주소값을 가지는 변수입니다.

포인터는 다음 형식으로 선언됩니다.
자료형* 포인터변수명;
예를 들어 
int a = 10;
int* b = &a;
int형 변수 a에 10을 담으면 이 10이라는 값은 메모리의 특정 주소에 기억될 것입니다. a라는 변수의 명칭은 그저 이 주소에 대한 별칭에 불과합니다.

그런뒤 int형 변수의 주소값을 담을 수 있는 b라는 포인터주소를 선언하고 &a로 a의 주소값을 대입하면 b는 a의 값이 아닌 주소를 가지게 됩니다. 여기서 &연산자는 피연산자에 해당하는 변수의 주소값을 가져오는 연산자입니다.

만약 이 상태에서
*b = 20;

라고 하면 a는 20값을 갖게 됩니다. 포인터 변수앞에 *은 포인터가 가리키고 있는 주소에 있는 실제 값을 가져오거나 설정할 수 있도록 하는데 이 값은 본래 a변수값인 10을 갖고 있다가 위 처리로 인해 20으로 바뀌게 됩니다.

2) new와 delete

일반적으로 C++에서 변수를 선언하면 이 변수의 데이터는 스택(stack)이라는 메모리 공간에 저장됩니다. 스택은 아래서 부터 위로 차곡차곡 데이터를 쌓는 형태로서 반대로 제거하는 경우에는 위에서 부터 아래로 하나씩 제거하는 형태로 동작합니다.

저장과 제거가 자동적이므로 개발자가 임의로 특정 변수에 대한 생명주기를 관리할 수 없습니다. 하지만 힙(heap)이라고 다른 메모리 구조에 변수를 저장할 수 있다면 스택과 다르게 임의로 생성된 변수를 제거할 수 있습니다.

int* b = new int;
위 구문에서 new는 피연산자로 지정된 자료형이나 클래스등의 생성자크기의 메모리 공간을 힙에 할당하고 그 주소를 반환하도록 합니다. 따라서 대입되는 변수는 포인터여야 합니다.
int* b = new int(30);
필요하다면 위에서 처럼 미리 특정 값을 초기화 할 수도 있습니다. 클래스도 같은 방법으로 힙에 할당할 수 있는데 예를 들어 아래와 같은 클래스가 존재하는 경우
class MyClass
{
public:
    int plus(int a, int b);
};

int MyClass::plus(int a, int b)
{
    return a + b;
}
이 클래스를 힙에 할당하려면
MyClass* p = new MyClass;
위와 같이 new연산자를 사용하면 됩니다. 이렇게 되면 기본생성자를 통해 힙에 할당하는데 별도로 정의한 생성자가 존재하고 이 생성자를 호출하고자 하는 경우 괄호를 열고 필요한 매개변수만 던져주면 됩니다.
MyClass* p = new MyClass(30);

생성된 포인터가 변수포인터의 경우에는 이미 말씀드린대로 *연산자로 값을 대입하거나 가져오면 되는데 클래스의 경우에는 해당 클래스의 멤버에 접근하려면 -> 연산자를 사용해야 합니다.

MyClass* p = new MyClass;
int result = p->plus(10, 20); //MyClass의 plus함수 호출
new연산자는 힙에 생성을 하는 연산자지만 delete는 반대로 해당 포인터가 사용중인 메모리를 해제하는 연산자입니다.
MyClass* p = new MyClass;
delete p; //new를 통해 생성된 포인터변수를 메모리에서 해제
만약 delete를 적절히 사용하지 않으면 메모리를 낭비할 수 있게 되므로 주의가 필요합니다.

3) 함수포인터

함수도 변수처럼 메모리에 위치하므로 포인터로 함수를 가리키는 것 또한 가능합니다. 함수포인터는 아래와 같은 형식으로 사용됩니다.
반환형 (*포인터명)(매개변수);
함수포인터는 함수의 반환형과 필요한 매개변수에 대한 정보를 필요로 합니다. 포인터를 통해 함수를 호출하는 경우에도 필요한 매개변수를 넘기고 값을 받아야 하기 때문입니다. 예를 들어 다음과 같은 함수가 있는 경우
int plus(int a, int b)
{
    return a + b;
}
이 함수에 대한 포인터를 선언하려면
int (*p)(int a, int b);
라고 한 뒤에
p = &plus;
함수에 대한 주소를 &연산자를 사용하여 가져와 포인터에 대입해주기만 하면 됩니다. 그리고 다음과 같이 포인터를 통해 일반적으로 함수를 호출할때 처럼 사용하면 됩니다.
int result = p(10, 20);
재미있는 것이 반환형식과 매개변수 형식만 맞으면(같다면) 전혀 다른 함수의 주소도 대입하여 사용할 수 있다는 것입니다.
int minus(int a, int b)
{
    return a - b;
}
위 함수는 plus함수와는 다른 동작을 하지만 plus함수의 반환형식과 매개변수의 갯수및 형식이 같습니다. 따라서 위에서 생성한 p 포인터에 주소를 대입하여 사용하는 것이 가능합니다.
int (*p)(int a, int b);

p = &plus; //plus함수 포인터
int result_plus = p(10, 20); //plus함수 호출

p = &minus; //minus함수 포인터
int result_minus = p(30, 10); //minus함수 호출

이제 까지의 함수 포인터는 전역적으로 정의된 함수만을 대상으로 하였습니다. 만일 포인터로 대입하고자 하는 함수가 클래스 내부에 있다면 함수포인터 생성시 '*포인터명'앞에 결합연산자(::)를 사용하여 클래스를 지정해 줘야 합니다.

반환형 (클래스::*포인터명)(매개변수);

그런데 클래스도 함수형식의 한 부분이기 때문에 반환형이나 매개변수등 함수형식이 같다해도 하나의 함수포인터로 여러클래스의 멤버함수를 가리킬 수는 없습니다.(물론 형식이 같다면 하나의 클래스에 대한 여러 멤버함수를 대입할 수는 있습니다.)

int (MyClass::*p)(int a, int b);

MyClass myclass;

p = &MyClass::plus;

int result = (myclass.*p)(10, 20);
클래스에 대한 멤버함수 포인터를 사용하는 방법은 먼저 MyClass의 함수포인터를 선언하고 MyClass의 plus함수에 대한 주소값을 포인터에 대입합니다. 그리고 실제 포인터를 사용해 함수를 호출할때는 MyClass의 객체인 myclass에서 호출해야합니다.

멤버함수는 객체별로 메모리에 생성되지 않고 클래스 레벨에서 메모리에 존재하는데(비정적멤버함수와 정적멤버함수 모두 정적메모리에 위치) 함수포인터가 객체의 멤버함수를 호출할 수 있게 함으로서 클래스 레벨에 있는 멤버함수의 메모리에 있는 코드를 실행하도록 해야하기 때문입니다.

 참조

참조를 사용하면 실제값은 포인터가 갖는 주소값이지만 마치 일반변수처럼 포인터를 사용할 수 있습니다.
int a = 10;
int& b = a;
위에서 사용된 &연산자는 참조연산자로서 변수 b는 변수 a의 int형 주소값을 갖는 참조형 변수에 해당합니다. 이 참조형 변수가 생성되면 일반 변수처럼 값을 가져오거나 대입할 수 있습니다.
b = 20;
물론 위와 같이 하면 변수 a의 값도 같이 바뀌기 때문에 참조형 변수도 포인터성격을 갖고 있다고 볼 수 있습니다.

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

[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
[Visual C++] 구조체  (0) 2012.07.05
[Visual C++] 함수  (0) 2012.07.04
2 0
Programming/C C++
특정 데이터형에 대한 별칭 부여가 가능하게 합니다. 예를 들어 int형을 IT라는 이름으로 정의하고자 할때
typedef int IT;

IT a = 10;

cout << a << endl;
위와 같이 구현할 수 있습니다.

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

[Visual C++] 연산자 오버로딩(Operator Overloading)  (0) 2012.10.05
[Visual C++] 포인터와 참조  (2) 2012.07.12
[Visual C++] typedef  (0) 2012.07.06
[Visual C++] 구조체  (0) 2012.07.05
[Visual C++] 함수  (0) 2012.07.04
[Visual C++] 연산자  (0) 2012.07.03
0 0
Programming/C C++
클래스와 비슷하게 멤버변수와 함수를 가질 수 있는 것으로 사용자 정의 자료형을 표현하는데 많이 사용됩니다.

구조체의 형식은 아래와 같습니다.
struct 구조체이름
{
    //멤버
};
적절한 구조체 이름을 부여하고 클래스와 같이 구조체 내부에 필요한 멤버를 정의합니다. 구조체도 클래스와 마찬가지로 마지막에 ;문자를 부여해야 합니다.
struct myStr
{
    int a;
    int b;

    int result()
    {
         return a + b;
    }
};
위와 같은 구조체가 정의되었을때 구조체 내부의 멤버는 다음과 같은 방법으로 접근이 가능합니다. 참고로 구조체의 멤버는 접근제한자가 특별히 설정되지 않으면 모두 public성격을 가집니다.
myStr mystr;
mystr.a = 10;
mystr.b = 20;

cout << "결과 : " << mystr.result() << endl;
구조체를 사용하려면 클래스 처럼 인스턴스를 생성(myStr mystr;)해야 하는데 구조체를 정의할때 바로 인스턴스를 생성하는 방법도 있습니다.
struct myStr
{
    int a;
    int b;

    int result()
    {
        return a + b;
    }
} mystr;

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

[Visual C++] 포인터와 참조  (2) 2012.07.12
[Visual C++] typedef  (0) 2012.07.06
[Visual C++] 구조체  (0) 2012.07.05
[Visual C++] 함수  (0) 2012.07.04
[Visual C++] 연산자  (0) 2012.07.03
[Visual C++] 흐름제어  (0) 2012.07.02
0 0
Programming/C C++
 함수의 형태

C++ 에서 함수는 다음과 같은 형태로 작성됩니다.
한정자 반환형 함수명 (매개변수1, 매개변수2...)
{
    처리문;

    return 반환값;
}
한정자는 함수에 대한 접근제한이나 메모리위치방식 또는 상속등을 지정하는 역활을 하며 생략이 가능한 부분입니다.

반환형은 함수가 처리된 뒤 어떠한 형의 데이터를 반환하는지 나타내는 부분입니다. 실제 데이터는 return의 반환값을 통해 반환되며 이때 반환값의 데이터형과 함수에 지정된 반환형은 타입이 일치해야 합니다. 다만 함수가 반환값을 가지지 않는 경우에는 함수의 반환형을 void로 지정하고 함수에서 return은 사용되지 않거나 반환값이 없는 형태인 return; 만 작성되어 함수처리를 단순히 종료하는 역활만 하게 됩니다.

함수명은 개발자가 함수가 하는 역활에 따라 적절하게 이름을 붙여주면 됩니다.

매개변수는 함수를 호출할때 함수에 값을 넘겨주는 부분으로 함수내에서 매개변수를 사용하여 필요한 데이터를 받아 처리할때 사용됩니다. 함수처리에 매개변수가 필요없는 경우 생략이 가능합니다.

 함수의 선언

다음은 두개의 매개변수를 받아 합계를 반환하는 함수의 예입니다. 이 함수에서 한정자는 생략되었습니다.
int plus(int a, int b)
{
    return a + b;
}
이 함수는 int형식의 데이터를 반환하는 plus라는 이름의 함수이며 int형 매개변수 두개(a와 b)를 받아 함수내에서 각 매개변수를 더한 값을 return을 통해 반환합니다.

그런데 다음과 같이 필요한 함수를 작성하고 사용하려면 컴파일 오류가 발생합니다.
int main()
{
   int result = plus(10, 20);

   return 0;
}

int plus(int a, int b)
{
   return a + b;
}
오류가 발생하는 원인은 컴파일러가 프로그램을 컴파일 할때 main에서 plus라는 함수사용을 발견하고 적절한 처리를 해야 하지만 이 함수가 실제 어떠한 형태를 가진 함수인지는 아직 확인되지 않았기 때문입니다.

그래서 컴파일러에게 함수를 사용하기전 어떠한 형태의 함수인지를 컴파일러에게 알려주어야 합니다. 그렇게 하기 위해서는 두가지 방법을 사용할 수 있는데 하나는 함수자체를 실제 함수가 호출되는 함수 위에 작성하는 것입니다.
int plus(int a, int b)
{
    return a + b;
}

int main()
{
    int result = plus(10, 20);

    cout << "결과값 : " << result << endl;    

    return 0;
}
위 예제는 main함수에서 plus함수를 호출하여 사용하고 main위에 plus함수 본체를 작성함으로서 컴파일러에게 미리 plus함수에 대해서 알 수 있도록 한것입니다.

또 다른 방법은 함수선언부분만 main위에 작성하고 본체는 따로 두는 방법입니다. 실제 컴파일러가 함수에 대해서 알아야할 부분은 함수가 어떤 매개변수를 받고 어떠한 형태의 데이터를 반환하는지만 알면 되기 때문에 이 모든 내용이 들어간 함수선언부분만 미리 작성되면 컴파일이 가능하기 때문입니다.
int plus(int a, int b);

int main()
{
    int result = plus(10, 20);

    cout << "결과값 : " << result << endl;

    return 0;
}

int plus(int a, int b)
{
    return a + b;
}
이전 예제와는 다르게 int plus(int a, int b); 라는 부분이 main() 함수 위에 있고 본체는 main() 함수 아래에 존재합니다. 함수를 작성할때 처음 작성되는 첫라인이 끝에 ;문자만 붙어서 그대로 main() 함수위에 선언된 것으로, 이렇게 사용자 함수 첫 부분만 함수 사용전에 미리 선언하면 실제 해당 함수의 본체가 어디에 있건 상관이 없이 사용이 가능하게 됩니다.

 기본 매개변수

함수를 호출할때 해당 함수에 매개변수가 있으면 함수의 매개변수에 값을 넣어 함수를 호출해야 합니다. 예를 들어 두개의 int형 매개변수를 받는 plus라는 함수가 존재하는 경우
int result = plus(10, 20);
위와 같이 호출할 수 있습니다. 그런데 만일 plus함수에서 두번째 매개변수의 값이 20으로 항상고정되거나 거의 변하지 않는 값인 경우 매번 함수를 호출할때마다 20이라는 값을 지정하는것은 번거로울 수 있습니다. 그래서 함수는 미리 값을 지정해 두는 기본매개변수라는 것이 존재합니다.
int plus(int a, int b = 20)
{
   return a + b;
}
plus함수에서 두번째 매개변수인 b에 20이라는 값을 대입하였습니다. 이렇게 하면 함수를 호출할때 b의 값이 20인 경우 해당 매개변수의 값을 생략하고 함수를 호출할 수 있습니다.
int result = plus(10);
매개변수가 하나만 지정된 경우 함수는 알아서 b값을 20으로 처리하게 됩니다. 물론 기존대로 두개의 매개변수를 모두 전달할 수 있는데 이런 경우 기본매개변수에서 지정한 값은 무시되고 호출할때의 값으로 처리하게 됩니다.

또한 기본매개변수 지정은 반드시 가장 오른쪽 매개변수부터 지정되어야 합니다. 그렇지 않고 무작위로 기본매개변수가 지정되면 함수를 호출할때 매개변수에 필요한 값을 생략한 채로 함수를 호출하면 어느 기본매개변수의 값을 무시해야 하는지 알 수 없기 때문입니다.

 오버로딩

오버로딩은 쉽게 말해 같은 이름의 함수를 여러개 존재시키는 것이라고 말할 수 있습니다.

예를 들어 두개의 매개변수를 받아 각 매개변수를 더한 값을 반환하는 plus라는 함수가 있는 경우
int plus(int a, int b)
{
    return a + b;
}

위 함수를 호출할때 매개변수가 int가 아닌 float형일 때에는 매개변수와 반환형식만 달리한 plus라는 이름의 함수를 새로 만들기만 하면 됩니다.

int plus(int a, int b)
{
    return a + b;
}

float plus(float a, float b)
{
    return a + b;
}
같은 처리(덧셈)를 하는 같은 이름(plus)의 함수를 매개변수와 반환형식만 달리한 채로 중복해서 작성할 수 있는 것으로 이것을 오버로딩이라고 합니다.

이와 같이 오버로딩 함수가 존재하면 함수호출시 매개변수를 통하여 어떠한 함수를 호출할지 구분하여 처리합니다.
 int result_i = plus(10, 20);
 float result_f = plus(0.25f, 0.3f);

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

[Visual C++] typedef  (0) 2012.07.06
[Visual C++] 구조체  (0) 2012.07.05
[Visual C++] 함수  (0) 2012.07.04
[Visual C++] 연산자  (0) 2012.07.03
[Visual C++] 흐름제어  (0) 2012.07.02
[Visual C++] 형변환  (0) 2012.06.29
0 0
Programming/C C++
 산술연산자

 +  덧셈
 -  뺄셈
 *  곱셈
 /  나눗셈
 %  나머지

예) int result = 3 + 4;

 증감연산자

 ++  1증가
 --  1감소

피연산자가 하나만 있는 단항연산자로 ++a; 처럼 사용되는 경우 선증가, a++; 처럼 사용되는 경우 후증가 연산을 수행합니다.

 쉬프트 연산자

 <<  왼쪽으로 비트 이동
 >>  오른쪽으로 비트 이동
int i = 10;
int j = i << 2;
위 예제의 경우 i변수의 2진수에서 왼쪽으로 2비트 만큼 이동한 값을 j에 대입합니다. 변수 j는 int형으로서 4byte인 32bit이고 이 비트를 왼쪽으로 2비트 만큼 밀고난 후 밀려난 부분을 0으로 채운 값(40)이 됩니다.

다만 오른쪽 쉬프트 연산자인 경우에는 피연산자가 음수이면 밀려난 자리에 0이 아닌 1의 값을 채우게 됩니다.

 논리연산자

 &&  논리곱(AND)
 ||  논리합(OR)

논리곱은 피연산자 모두 참(true)이여야 참(true)값을 반환하며 논리합은 피연산자중 하나만 참이여도 참의 값을 반환합니다.

 비트연산자

 &  논리곱(AND)
 |  논리합(OR)
 ^  베타적논리합(XOR)

피연산자의 비트값을 대상으로 연산을 수행하는 연산자입니다. 예를 들어 정수 3의 비트는 0011이고 정수 2의 비트는 0010 이므로 이 두개의 비트를 & 연산하게 되면 다음과 같은 결과값을 얻게 됩니다.

0011
0010
---- &
0010 -> 결과

참고로 ^ 연산자는 피연산자중 단 하나만 참(true)의 값을 가져야만 참이 되는 연산입니다.

0011
0010
---- ^
0001

 관계연산자

 <  오른쪽이 왼쪽보다 크다.  <=  오른쪽이 왼쪽보다 크거나 같다.
 >  왼쪽이 오른쪽 보다 크다.  >=  왼쪽이 오른쪽보다 크거나 같다.
 ==  같다.  !=  다른다.

연산의 결과가 0이면 false, 1이면 true

 대입연산자

 =  오른쪽 값을 왼쪽에 대입  +=  오른쪽 값을 왼쪽에 더함
 -=  오른쪽 값을 왼쪽값에 감산  *=  오른쪽 값을 왼쪽값에 곱함
 /=  오른쪽 값을 왼쪽값에 나눔  %=  오른쪽 값을 나눈 나머지를 왼쪽에 대입
 <<=  왼쪽값을 오른쪽 값만큼 왼쪽 쉬프트 수행  >>=  왼쪽값을 오른쪽 값만큼 오른쪽 쉬프트 수행
 &=  오른쪽 값의 비트 논리곱 결과를 왼쪽에 대입  ^=  오른쪽 값의 비트 베타적 논리합 결과를 왼쪽에 대입
 !=  오른쪽 값의 비트 논리합 결과를 왼쪽에 대입    

예)
int a = 0;
a += 1;

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

[Visual C++] 구조체  (0) 2012.07.05
[Visual C++] 함수  (0) 2012.07.04
[Visual C++] 연산자  (0) 2012.07.03
[Visual C++] 흐름제어  (0) 2012.07.02
[Visual C++] 형변환  (0) 2012.06.29
[Visual C++] 나열형(enum)  (0) 2012.06.28
0 0
Programming/C C++
 조건문

(1) if

if 문의 기본 형식은 다음과 같습니다.
if (식)
    실행문;
식의 결과가 참(true)이면 그 아래 실행문 부분이 실행됩니다. 만약 식의 결과가 거짓(false)인 경우에 별도의 처리가 필요하다면 else 구문을 사용합니다.
if (식)
    실행문;
else
    실행문;
C++ (C로 부터 파생된 대부분의 언어가 마찬가지) 에서 거의 모든 흐름제어문은 그 아래 단 하나의 문장만을 제어문의 제어범위로 판단합니다. 따라서 여러 문장의 실행문 전체를 하나의 제어범위에 두려면 중괄호({와 })를 사용해야 합니다.
if (식) {
    실행문1;
    실행문2;
}
else {
    실행문3;
    실행문4;
    실행문5;
}
if문에 사용되는 식(조건)이 많은 경우 else if 문을 사용해 다양한 실행의 조건을 지정할 수 있습니다.
if (식1)
    실행문;
else if (식2)
    실행문;
else if (식3)
    실행문;
else
    실행문;
식1 의 결과가 거짓인 경우 식2를.. 식2가 거짓인 경우 식3을 확인하고 모든 식이 거짓이면 마지막 else 아래 실행문을 실행하게 됩니다.

(2) switch

switch 문의 기본형식은 다음과 같습니다.
switch(식)
{
    case 값1:
        실행문1;
        break;
    case 값2:
        실행문2;
        실행문3;
        break;
    case 값3:
        실행문4;
        break;
    default:
        실행문5;
}
switch 문의 괄호에 들어간 식의 값이 값1에 해당하면 case 값1 아래에 실행문 실행하고 값2에 해당하면 case 값2 아래의 실행문2와 3을 실행합니다. case아래 실행문은 여러 문장이라해도 중괄호({})의 지정이 불필요 하며 각 case의 값은 반드시 세미콜론(;)이 아닌 : 으로 지정되어야 합니다.

case아래 break; 문은 실행을 정지시키는 역활을 하는데 만약 위 예제에서 case 값2 아래에 break;가 존재하지 않으면 case 값3 부터 그 아래 모든 실행문을 실행하게 됩니다.

마지막 default문은 식이 어떠한 case 에도 해당되지 않는 경우 실행하게 되는 부분입니다. default는 필요없다면 생략이 가능합니다. 또한 default에는 break;문도 존재하지 않는데 어차피 그 아래 더이상 실행할 구문이 없기 때문입니다.

 반복문

(1) for

for 문의 기본형식은 다음과 같습니다.
for (초기화; 조건; 반복)
    실행문;
for문은 우선 초기화 문장을 실행한뒤 조건부분을 판단하고 조건이 참(true)이면 그 아래 실행문을 실행한 뒤 반복을 수행하는 순서로 동작하게 됩니다.

예를 들어
for (int i = 0; i <= 10; i++) {
    cout << "현재 i의 값 : " << i << endl;
}
위 예제에서 for는 우선 i의 값을 0으로 초기화 한 후 실행 조건을 판단합니다. 실행 조건은 i <= 10 이므로 i는 10보다 작거나 같아야 결과는 true가 됩니다. 따라서 i의 값은 0이기 때문에 for안에 cout로 시작하는 실행문을 실행합니다.

실행문을 한번 실행하고 나면 이번에는 반복부분의 문장을 실행합니다. for에서 반복은 i++이므로 i의 값을 1증가 시킵니다.

그리고 다시 조건을 확인하고 실행문을 실행하는 식으로 반복을 수행합니다.

(2) while

while 문의 기본형식은 다음과 같습니다.
while (조건)
    실행문;
while에서 조건은 해당 실행문을 실행할지 여부를 판단하는 역활을 합니다. 예를 들어
int i = 0;
while (i <= 10) {
    cout << "현재 i의 값 : " << i << endl;
   
    i++;
}
위 예제의 경우 우선 변수 i의 값을 0으로 초기화 하고 while문을 실행합니다. i의 값은 0이므로 i <= 10 조건에 true가 됩니다. 따라서 while안에 문장을 실행합니다.

while안에는 i++; 로 i의 값을 증가시켜 주고 있는데 만약 이 문장이 없으면 i의 값은 계속 0이 되므로 흔히 '무한루프'라고 하는 무한대의 반복에 빠지게 됩니다. while문을 사용할때 가장 주의해야할 부분 입니다.

(3) do while

while와 do while의 차이는 조건판단입니다. while는 실행문을 실행하기 전에 조건을 판단하지만 do while문은 실행문을 반드시 한번은 실행하고 난뒤 계속 반복할지 여부를 조건으로 판단하게 됩니다.
do {
    실행문; //반드시 한번은 실행함
} while (조건)
 점프문

(1) goto

goto는 지정된 레이블로 실행의 흐름을 이동시키는 구문입니다.
int i = 0;
while (i <= 10) {
    cout << "현재 i의 값 : " << i << endl;

    i++;

    if (i == 9)
        goto nine;
}

nine:
    cout << "i의 값이 9가 됨" << endl;
while에서 i문을 1씩 증가시켜 가며 i가 11이 될때까지(i <= 10 이므로 i가 10이 되어도 true입니다.) 반복수행 하다가 i가 9가 되는 경우 if문의 의해서 goto문이 실행됩니다.

goto는 nine;로 레이블을 지정하고 있고 따라서 프로그램의 흐름은 while문을 벗어나 아래 nine: 라는 부분으로 이동하여 그 아래 문장을 실행하게 되는 것입니다.

(2) break; / continue;

break;는 반복문의 흐름을 완전히 중단시키는 역활을 하며 continue;는 단 한번 스킵하는 역활을 합니다.
int i = 0;
while (i <= 10) {
    cout << "현재 i의 값 : " << i << endl;

    i++;

    if (i == 5)
        break;
}
위 while문에서는 if 문을 통해 i가 5값인 경우 break;문을 지정하였습니다. 따라서 i가 5가 되면 while 처리가 완전히 중단되고 while문을 빠져 나오게 됩니다.
int i = 0;
while (i <= 10) {
    i++;
    if (i == 5)
        continue;

    cout << "현재 i의 값 : " << i << endl;
}
위 예제와 같으나 break;대신 continue;문이 지정되었고 i가 5인지를 판단하는 if가 실제 처리문 위에 있습니다. 이 예제는 i가 5가 되는 경우 while 처리를 중단하는 것이 아니라 그 아래 실행문을 한번만 건너뛸 뿐입니다.


(3) return;

return은 주로 함수내에서 특정한 값을 반환하고 함수의 처리를 중단하는 용도로 사용됩니다.

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

[Visual C++] 함수  (0) 2012.07.04
[Visual C++] 연산자  (0) 2012.07.03
[Visual C++] 흐름제어  (0) 2012.07.02
[Visual C++] 형변환  (0) 2012.06.29
[Visual C++] 나열형(enum)  (0) 2012.06.28
[Visual C++] 상수  (0) 2012.06.27
0 0
Programming/C C++
C++ 에서 형변환은 다음과 같이 이루어 집니다.
int i = 100;

float f = (float)i;
특정 데이터를 괄호를 통해 변환하고자 하는 데이터형으로 지정하면 됩니다.

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

[Visual C++] 연산자  (0) 2012.07.03
[Visual C++] 흐름제어  (0) 2012.07.02
[Visual C++] 형변환  (0) 2012.06.29
[Visual C++] 나열형(enum)  (0) 2012.06.28
[Visual C++] 상수  (0) 2012.06.27
[Visual C++] 자료형  (2) 2012.06.26
0 0
Programming/C C++
나열형은 값을 특정 요소의 값으로 제한하는 특징을 가집니다. 예를 들어 일요일 부터 토요일까지의 요일을 표현하는 데이터형을 나열형으로 표현하면 아래와 같이 작성될 수 있습니다.
enum days {
  sun,
  mon,
  tue,
  wed,
  thu,
  fri,
  fat
 };
나열형 안에 포함된 각각의 목록( sun, mon...)은 , 문자로 구분되며 특정한 값을 지정하지 않으면 내부적으로 0부터 시작됩니다. 즉, sun = 0 과 같습니다.

필요한 경우 나열형의 내부값은 직접 지정할 수 있으며 그 다음 요소에 값이 지정되지 않았을 경우 이전값 부터 +1씩 증가한 값을 갖게 됩니다.
enum days {
  sun = 10,
  mon = 11,
  tue,
  wed,
  thu,
  fri,
  fat
 };
따라서 위의 경우 tue는 12의 값을 갖습니다.

위에서 처럼 선언한 나열형은 다음과 같이 나열형의 데이터형 변수를 선언하고 각 요소의 값을 대입하여 사용하게 됩니다.
days d = thu;

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

[Visual C++] 흐름제어  (0) 2012.07.02
[Visual C++] 형변환  (0) 2012.06.29
[Visual C++] 나열형(enum)  (0) 2012.06.28
[Visual C++] 상수  (0) 2012.06.27
[Visual C++] 자료형  (2) 2012.06.26
[Visual C++] 변수  (0) 2012.06.25
0 0
Programming/C C++
상수는 한번 정해진 값을 바꿀 수 없는 변수입니다.
const int a = 10;
C++에서 상수는 const를 키워드를 사용하며 'const 데이터형 변수명 = 값;'의 형태로 사용됩니다. 상수는 선언될때 값을 지정해야 하며 한번 지정한 값은 바꿀 수 없습니다.

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

[Visual C++] 형변환  (0) 2012.06.29
[Visual C++] 나열형(enum)  (0) 2012.06.28
[Visual C++] 상수  (0) 2012.06.27
[Visual C++] 자료형  (2) 2012.06.26
[Visual C++] 변수  (0) 2012.06.25
[Visual C++] C++ 기본 형태  (0) 2012.06.22
0 0
Programming/C C++
 정수형

 char  1 byte  -128 ~ 127
 unsigned char  1 byte  0 ~ 255
 bool  1 byte  true / false
 short  2 bytes  -32,768 ~ 32,767
 unsigned short  2 bytes  0 ~ 65,535
 int  4 bytes  -3,147,483,648 ~ 2,147,483,647
 unsigned int  4 bytes  0 ~ 4,294,967,295
 long  4 bytes  -2,147,483,648 ~ 2,147,483,647
 unsigend long  4 bytes  0 ~ 4,294,967,295
 long long  8 bytes  -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
 unsigned long long  8 bytes  0 ~ 18,446,744,073,709,551,615

unsigned 는 양수만 가지는 데이터형으로 -로 표현할 음수의 범위를 양수쪽으로 확장시킴으로서 양수를 기존보다 더 넓게 사용할 수 있도록 합니다.

 부동소수점형

 float  4 bytes  3.4E +/- 38 (7 digits)
 double  8 bytes  1.7E +/- 308 (15 digits)
 long double  8 bytes  1.2E +/- 4932 (19 digits)

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

[Visual C++] 나열형(enum)  (0) 2012.06.28
[Visual C++] 상수  (0) 2012.06.27
[Visual C++] 자료형  (2) 2012.06.26
[Visual C++] 변수  (0) 2012.06.25
[Visual C++] C++ 기본 형태  (0) 2012.06.22
[Visual C++] 관련 라이브러리  (0) 2012.06.21
2 0
Programming/C C++
C++ 에서 변수는 아래와 같이 선언합니다.
int age = 0;

int month;
month = 6;
변수는 '데이터형 변수명' 형태로 선언하며 변수는 반드시 사용전에 초기화 되어야 합니다.

변수나 함수, 클래스등에 붙이는 이름을 식별자라고 하는데 이 식별자는 알파벳과 숫자 그리고 _ 문자만 사용이 가능합니다. 숫자는 사용할 수 있으나 식별자의 시작문자로는 사용할 수 없으며 예약어(C++ 내에서 특별한 의미를 가지는 단어) 또는 사용이 불가하지만 예약어를 포함하는 형태의 단어인 경우는 사용이 가능합니다.

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

[Visual C++] 상수  (0) 2012.06.27
[Visual C++] 자료형  (2) 2012.06.26
[Visual C++] 변수  (0) 2012.06.25
[Visual C++] C++ 기본 형태  (0) 2012.06.22
[Visual C++] 관련 라이브러리  (0) 2012.06.21
표준 Library 함수 - Memory 관련 함수  (0) 2011.06.16
0 0
Programming/C C++
#include <iostream>

using namespace std;

int main()
{
    /* my program */
    cout << "hello Visual C++" << endl;

    return 1;
}
#include 는 소스파일을 컴파일 하기전 <와 >부분에 명시된 파일을 해당 소스파일에 추가시키는 전처리문입니다. 전처리는 컴파일전에 어떠한 처리를 선행하는 부분으로서 대부분 #으로 시작하며 ;문자가 없는 특징이 있습니다.

참고로 <와 >부분에 명시된 파일은 C++ 표준 라이브러리 헤더파일을 참조시킬때 사용하는데 ANSI/ISO 표준에 따라 C++은 기본적으로 몇몇 함수와 클래스 라이브러리를 제공하고 있습니다.

만일 개발자가 직접 작성한 헤더파일 이라면 경우는 <> 대신 "와 "문자를 사용해 지정합니다.

using namespace std;문은 C++ 표준라이브러리에 있는 std 네임스페이스 사용을 선언하는 것으로 std 네임스페이스에 있는 식별자 사용을 의미합니다.(예를 들면 cout 와 같은...)

물론 네임스페이스 사용을 선언하지 않을 수도 있는데 이런 경우에는 std::cout와 같이 함수명에 네임스페이스를 붙여서 사용해야 합니다.

main() 은 C++ 프로그램의 시작 진입점 함수입니다. 무조건 main 함수로 부터 시작되므로 프로그램은 반드시 하나의 main 함수를 가져야 합니다. main 함수 앞에 int는 이 함수의 실행이 끝나는 경우 int형의 값을 반환함을 의미합니다.

/* 로 시작해 */로 끝나는 부분은 주석입니다. 프로그램에 설명을 붙이는 부분으로서 단 한줄만을 주석처리하는 //도 사용할 수 있습니다.

cout는 iostream에 정의된 표준화면출력 함수입니다. 이 함수는 << 로 지시된 내용을 화면으로 출력합니다.

endl은 줄바꿈문자를 출력합니다.

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

[Visual C++] 자료형  (2) 2012.06.26
[Visual C++] 변수  (0) 2012.06.25
[Visual C++] C++ 기본 형태  (0) 2012.06.22
[Visual C++] 관련 라이브러리  (0) 2012.06.21
표준 Library 함수 - Memory 관련 함수  (0) 2011.06.16
[C, C++] Class구현시 생성자와 소멸자 활용  (0) 2011.06.01
0 0
Programming/C C++
 MFC (Mircrosoft Foundation Classes)

Win32 API(Application Programming Interface), 리스트/큐등의 자료구조 컬렉션, DAO/ODBC/OLEDB등과 같은 데이터베이스등 윈도우 응용 프로그램 개발에 필요한 거의 모든 클래스 라이브러리를 제공

 ATL (ActiveX Template Library)

COM (Component Object Model) 컴포넌트 혹은 ActiveX 개발이나 응용프로그램의 컴포넌트 개발 라이브러리로서 COM+ Object나 ActiveX Control 을 결합해 하나의 애플리케이션을 완성

 .NET Framework

C++/CLI .NET 규격을 이용한 C++개발가능, 이 경우 .NET Framework의 풍부한 라이브러리를 사용

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

[Visual C++] 변수  (0) 2012.06.25
[Visual C++] C++ 기본 형태  (0) 2012.06.22
[Visual C++] 관련 라이브러리  (0) 2012.06.21
표준 Library 함수 - Memory 관련 함수  (0) 2011.06.16
[C, C++] Class구현시 생성자와 소멸자 활용  (0) 2011.06.01
[C, C++] Class  (0) 2011.05.25
0 0
Programming/C C++
아래 함수사용을 위해서는 string.h Header File선언이 필요합니다.

 함수  사용  설명
 memchr()  p = memchr(p1, c1, ib);  s1이 지시하고 있는 memory영역의 data중 c1에 해당하는 문자를 ib(byte단위)단위로 검색하고 검색된 위치부터의 memory주소를 pointer로 반환합니다.
 memcmp()  i - memcmp(p1, p2, ib);  s1과 s2가 지시하고 있는 memory영역의 data를 ib(byte단위)만큼 비교하고 같으면 0을 반환합니다.

각 함수의 인수에서 p1, p2는 Memory시작주소를 갖고 있는 Pointer입니다. c1은 문자단위 인수이며 ib는 정수형태의 인수를 의미합니다.

#include <stdio.h>
#include <string.h>

main()
{
  char s[12];
  char *p;
 
  strcpy(s, "hello!world");
 
  printf("%s\n", s);
 
  p = memchr(s, '!', 8);
 
  printf("%s\n", p);
}


memchr()함수를 통해 s의 Memory영역을 8Byte단위로 검색하여 '!'를 찾습니다. 만일 검색에 성공하면 '!'가 위치하는 주소값을 Pointer p에 반환합니다.(검색한 결과가 없으면 Null이 반환됩니다.)


#include <stdio.h>
#include <string.h>

main()
{
  char s1[5] = "abcde";
  char s2[6] = "abcde";
 
  printf("%d\n", memcmp(s1, s2, 5));
 
  strcpy(s1, "aacde");
 
  printf("%d\n", memcmp(s1, s2, 5));
 
  strcpy(s2, "aaade");
 
  printf("%d\n", memcmp(s1, s2, 5));
}


memcmp()함수를 세번 비교하여 각각의 결과값을 확인하고 있습니다.


결과에서 보듯이 두 인수가 같으면 0을 반환하지만 만일 s1(왼쪽)이 더 크면 1을 s2(오른쪽)가 더 크면 -1을 반환합니다.

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

[Visual C++] C++ 기본 형태  (0) 2012.06.22
[Visual C++] 관련 라이브러리  (0) 2012.06.21
표준 Library 함수 - Memory 관련 함수  (0) 2011.06.16
[C, C++] Class구현시 생성자와 소멸자 활용  (0) 2011.06.01
[C, C++] Class  (0) 2011.05.25
[C, C++] cout 서식지정  (0) 2011.04.14
0 0
Programming/C C++
Class사용시에는 Class의 Object를 생성한 후 각 Member함수를 호출하여 Member변수의 값을 설정합니다.

이와는 달리 Class의 생성자는 Object생성과 동시에 원하는 Member변수를 초기화 할 수 있습니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    exam(char *nm, int k, int m, int e);
   
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

exam::exam(char *nm, int k, int m, int e)
{
  strcpy(name, nm);
 
  kuk = k;
  mat = m;
  eng = e;
}

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

main()
{
  exam student("young soo", 90, 100, 80);
 
  student.memberprint();
  return 0;
}


Class안에 exam(char *nm, int k, int m, int e); 으로 생성자를 선언하고 main안에서 exam student("young soo", 90, 100, 80); 구문을 통해 생성자로 초기값을 설정합니다.

생성자의 선언은 Class이름과 동일하게 하고 인수를 설정하여 어떠한 Member에 값을 설정할지를 지정하면 됩니다. 생성자의 본체도 Class이름으로 다음과 같이 작성되어 해당 Member에 대해 초기화를 수행하도록 합니다.
exam::exam(char *nm, int k, int m, int e)
{
  strcpy(name, nm);
 
  kuk = k;
  mat = m;
  eng = e;
}

생성자는 초기화 전용입니다. 따라서 return을 통해 값을 반환할 수 없습니다. 반환값없는 전용이므로 이 생성자함수에 void나 int처럼 return형식을 지정하는 것 자체가 무의미한 것입니다.

생성자를 통해 값을 초기화 하려면 Class의 Object생성시 바로 인수를 건네주도록 합니다. 그러면 자동으로 생성자가 호출되어 해당 Class에 대한 초기화처리를 하게 됩니다.

exam student("young soo", 90, 100, 80);

소멸자는 Class이름앞에 ~문자를 붙여 선언하는 것으로 이 소멸자는 Class의 Member함수를 호출하고 그 함수를 빠져나올때 자동으로 호출되는 특징을 가지고 있습니다.

#include <iostream.h>
#include <string.h>
class exam{
  private:
    char name[15];
 
  public:
    exam(char *nm);
    ~exam();
   
    void name_input(char *nm);
    void memberprint();
};

exam::exam(char *nm)
{
  strcpy(name, nm);
}

exam::~exam()
{
  cout << "소멸자가 호출됨" << endl;
}

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
}

main()
{
  exam student("young soo");
 
  student.memberprint();
  return 0;
}


Class안에 ~exam();으로 소멸자를 선언하였습니다. 이 소멸자는 Class내의 함수가 호출되고 난 후마다 자동 호출됩니다.


소멸자는 어떤값도 반환하지 않고 또 어떠한 인수도 전달하지 않습니다. 또한 소멸자를 정의하는 함수부분에는 개발자가 Class의 함수호출이 이루어지고 난 다음에 필요한 부분을 작성하기만 하면 되는 것입니다.

1. 생성자 Overloaded

C++에서의 함수는 Overloaded가 가능합니다. 즉, 같은 이름의 함수에 다른 인수의 특성에 따라 다른처리를 행하는 것이 가능한 것입니다.

이러한 Overloaded는 일반 함수뿐만 아니라 생성자에도 적용할 수 있습니다. 물론 생성자에 Overloaded를 선언하는 방법도 일반함수와 다를게 없습니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    exam();
    exam(char *nm, int k, int m);
    exam(char *nm, int k, int m, int e);
   
    void memberprint();
};

exam::exam()
{
  strcpy(name, "noname");
 
  kuk = 0;
  mat = 0;
  eng = 0;
}

exam::exam(char *nm, int k, int m)
{
  strcpy(name, nm);
 
  kuk = k;
  mat = m;
  eng = 200;
}

exam::exam(char *nm, int k, int m, int e)
{
  strcpy(name, nm);
 
  kuk = k;
  mat = m;
  eng = e;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl << endl;
}

main()
{
  exam student_fir;
  student_fir.memberprint();
 
  exam student_sec("young soo", 90, 100);
  student_sec.memberprint();
 
  exam student_thr("soo young", 90, 100, 50);
  student_thr.memberprint();
  return 0;
}


Class에서 exam세개를 선언하였으며 각각의 함수마다 인수를 틀리게 하여 Overloaded화 하였습니다. Class에서 생성자를 Overloaded로 선언하면 각 함수에 맞는 정의도 다음과 같이 해주어야 합니다.

exam::exam()
{
  strcpy(name, "noname");
 
  kuk = 0;
  mat = 0;
  eng = 0;
}

exam::exam(char *nm, int k, int m)
{
  strcpy(name, nm);
 
  kuk = k;
  mat = m;
  eng = 200;
}

exam::exam(char *nm, int k, int m, int e)
{
  strcpy(name, nm);
 
  kuk = k;
  mat = m;
  eng = e;
}


이렇게 하면 생성자를 호출할때마다 해당 인수에 따라서 다른 형태로 초기화를 수행할 것입니다.

주의할 것은 Class의 Object를 생성할때 다른 Overloaded 생성자를 호출하고자 한다면 다음과 같이 다른 이름의 Object를 사용해야 한다는 것입니다.

exam student_fir;
student_fir.memberprint();
 
exam student_sec("young soo", 90, 100);
student_sec.memberprint();
 
exam student_thr("soo young", 90, 100, 50);
student_thr.memberprint();


만일 Class의 Object가 배열로 생성되면 []를 이용해 몇번째 배열의 Object에서 Member를 호출할지를 지정해야 하는데 이때 위와 같은 생성자의 특징을 이용하면 {}를 사용해 배열의 Member를 한꺼번에 초기화 할 수도 있습니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    exam(char *nm, int k, int m, int e);
   
    void memberprint();
};

exam::exam(char *nm, int k, int m, int e)
{
  strcpy(name, nm);
 
  kuk = k;
  mat = m;
  eng = e;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl << endl;
}

main()
{
  exam student[2] = {
    exam("soo young", 90, 100, 50),
    exam("young soo", 59, 65, 88)
  };
 
  student[0].memberprint();
  student[1].memberprint();
  return 0;
}


exam Class의 student Object를 2로 하여 2개의 배열로 Object가 생성되도록 하였습니다.

이후 초기화는 {와 }사이에 묶어 exam("soo young", 90, 100, 50)과 exam("young soo", 59, 65, 88)으로 각각의 배열에 이름과 점수를 초기화 합니다.

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

[Visual C++] 관련 라이브러리  (0) 2012.06.21
표준 Library 함수 - Memory 관련 함수  (0) 2011.06.16
[C, C++] Class구현시 생성자와 소멸자 활용  (0) 2011.06.01
[C, C++] Class  (0) 2011.05.25
[C, C++] cout 서식지정  (0) 2011.04.14
[C, C++] 구조체  (1) 2011.04.13
0 0
Programming/C C++
C와 C++의 차이중 가장 큰 것이 바로 Class개념입니다. 현재에도 Class는 java나 C#그리고 VB.NET에까지 다양한 언어에서 객체지향프로그래밍(OOP)으로 Class개념이 사용되고 있습니다.

C++에서 Class는 원래 C의 구조체를 좀더 효휼적으로 바꾸고자 해서 등장했다는 얘기가 있습니다. 실제로 정말 그런지는 확실하지 않지만 만일 C에서 구조체를 접해보신 경우라면 'C의 구조체에 변수 Member와 더불어 함수까지도 포함시킬 수 있게 한것이 Class다.' 라고 이해(꼭 그렇다는 것은 아니지만...)하시면 될듯 합니다.

다음은 C++에서 구조체를 활용한 예입니다.

#include <iostream.h>
#include <string.h>

main()
{
  struct exam{
    char name[15];
    int kuk;
    int mat;
    int eng;
  };
 
  exam student;
 
  strcpy(student.name, "kim young soo");
  student.kuk = 90;
  student.mat = 100;
  student.eng = 80;
 
  cout << "학생이름 : " << student.name << endl;
  cout << "국어점수 : " << student.kuk << endl;
  cout << "수학점수 : " << student.mat << endl;
  cout << "영어점수 : " << student.eng << endl;
 
  return 0;
}


exam구조체를 선언하고 각 변수 Member에 값을 설정한뒤 설정한 값을 확인하는 Program입니다.


Pogram에서 쓰인 구조체를 Class화 하려면 다음과 같이 바뀔 수 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

위 구조체를 class로 바꾼것으로서 exam을 Class로 정의하였습니다. 이 Class안에 name과 kuk, mat, eng라는 Member변수와 더불어 name_input, memberprint, kuk_input, mat_input, eng_input이라는 Member함수도 같이 존재하도록 하였습니다.

여기서 Member함수는 위 Member변수를 설정할 수 있도록 하기 위해 정의한 것입니다. (Memberprint()함수는 각 설정값을 확인하기 위한 용도로 사용하려고 합니다.)

위 Class에서 private와 public은 현재 Class에서 어떤 Member에 대해 접근 가능하게 할지 또 어떤 Member에 대해서 접근을 막을지에 대해 지정하는 것이라고 보시면 됩니다. 이때 private은 해당 Member에 대해 접근을 불가능하게 하는 Keyword이며 public는 접근을 허락하는 Keyword입니다.(protected 라는 Keyword가 있지만 이는 잠시 후 설명드리도록 하겠습니다.)

물론 Member변수에 public Keyword를 쓴다면 접근이 가능하게 할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);

    int eng;
};


eng Member변수를 public하위에 정의하였습니다.

student.eng = 90;

public으로 정의된 eng는 직접접근이 가능합니다.

private의 경우에는 생략할 수도 있습니다. 이는 처음 Member는 private로 처리하는 것이 기본으로 되어 있기 때문에 가능한 것입니다.

class exam{
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

이렇게 생략하면 다른 Keyword가 등장할때까지 모든 Member는 private로 처리됩니다.

이와 더불어 각 Keyword를 반복해서 쓸 수도 있습니다. 이는 해당 Member를 특성에 따라 Group으로 구분하고자 할때 유용하게 사용됩니다.

class exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
 
  public:
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
   
  private:
    int kuk;
    int mat;
    int eng;
};

private과 public Keyword는 여러번 쓸 수 있습니다.

Class안의 Member변수를 private에 둘것인가 public에 둘것인가 또는 Member함수를 private에 둘것인가 public에 둘것인가 하는 문제는 어디까지나 개발자의 자유이므로 어떤형태로든지 작성될 수 있습니다.

다음은 위 Class를 실제 Program적용시킨 예입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

main()
{
  exam student;
 
  int kukp;
  int matp;
  int engp;
 
  student.name_input("youngsoo");
 
  kukp = student.kuk_input(80);
  matp = student.mat_input(91);
  engp = student.eng_input(94);
 
  student.memberprint();

  cout << "국어점수(가산) : " << kukp << endl;
  cout << "수학점수(가산) : " << matp << endl;
  cout << "영어점수(가산) : " << engp << endl;
 
  return 0;
}


먼저 Program상단에 exam이라는 Class를 정의하였습니다. 이렇게 선언된 Class를 실제 사용하기 위해서는 Class의 Object를 생성해야 하며 생성 방법은 구조체를 사용할때와 비슷합니다.

exam student;

exam Class에서 student의 Object를 생성합니다.

보시는 바와 같이 Class는 Object생성해야 사용할 수 있습니다. Class에 바로 접근할 수 없다는 얘기인데 그 이유는 어느 한쪽에서 Class Member의 값을 수정하면 다른쪽에도 영향을 받을 수 있기 때문으로 Class를 사용할때는 Class자체를 특정이름으로 복사해서 사용해야할 필요성이 있는 것입니다. 이 Class의 복사본을 생성하는 과정을 흔히 Instance라고 하며 Instance에 의해 생성된 것(여기서는 student)을 Object(객체)라고 합니다.

위 Class는 Member변수와 Member함수를 가지도록 하였고 모든 Member변수는 private을 모든 Member함수는 public Keyword를 사용하였습니다. 따라서 Class의 Member변수에는 직접적으로 접근할 수 없지만 Member함수는 직접 접근이 가능합니다.

앞서 말씀드렸듯이 Class의 Member에는 접근이 불가능하므로 구조체를 사용할때 처럼 직접 값을 설정하는 것은 불가능합니다.

student.kuk = 90;

kuk Member변수는 private에 속하므로 위와 같이 값을 설정할 수 없습니다.

Class를 제대로 사용하기 위해서는 어떻게든 값을 설정해야할 필요가 있습니다. 그래서 public하위에 값을 설정할 수 있는 함수를 정의한 것입니다.

name_input, memberprint, kuk_input, mat_input, eng_input 함수는 public로 선언되었으므로 private로 선언된 Member변수와는 달리 직접 접근이 가능합니다. 이렇게 선언된 Member함수는 실제 이 함수가 어떤 기능을 수행할지 그 본체를 구현해야 하며 이때 구현은 Class밖에서 다음과 같은 방식으로 정의됩니다.

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}


Class안에 선언된 함수는 위와 같이 그 본체를 구현합니다.

위와 같은 방식은 Class의 특정 Member변수를 설정할때 꽤 유용하게 사용될 수 있는 부분입니다. 예를 들어 국어시험에서 모든 문제를 틀리면 0점이 됩니다. 시험을 아무리 못봐도 0이하(-값)은 될 수 없는데 만일 kuk이라는 Member변수에 -100을 설정하게 된다면 이것 자체가 오류가 됩니다.

이러한 상황이 생기는 것을 막으려면 kuk_input()함수를 다음과 같이 수정해야 합니다.

int exam::kuk_input(int pt)
{
  if (pt < 0){
    cout << "점수설정에 오류가 있습니다." << endl;
    return 0;
  }
 
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

pt값이 0이하이면 kuk member변수에 값을 설정할 수 없도록 합니다. 즉, 특정값에 따라 원하는 처리를 수행할 수 있도록 할 수 있는 것입니다.

또한 Class구현시 Class에서 선언된 각 Member함수와 실제 함수의 본체를 별도로 하는것이 불편하다면 다음과 같이 아예 Class안에서 모든것을 해결할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm){
      strcpy(name, nm);
    }
   
    void memberprint(){
      cout << "학생이름 : " << name << endl << endl;
      cout << "국어점수 : " << kuk << endl;
      cout << "수학점수 : " << mat << endl;
      cout << "영어점수 : " << eng << endl;
    }
   
    int kuk_input(int pt){
      int ptp = pt;
 
      if (pt >= 90)
        ptp = pt + 3;
 
      kuk = pt;
 
      return ptp;
    }
   
    int mat_input(int pt){
      int ptp = pt;
 
      if (pt >= 90)
        ptp = pt + 3;
 
      mat = pt;
 
      return ptp;
    }
   
    int eng_input(int pt){
      int ptp = pt;
 
      if (pt >= 90)
        ptp = pt + 3;
 
      eng = pt;
 
      return ptp;
    }
};

Class안에서 함수구현까지 모두 처리하고 있습니다.

위 함수에서 쓰인 exam:: 부분은 exam Class의 함수를 구현하겠다는 의미가 됩니다. 이렇게 함수를 구현한뒤 구조체처럼 함수를 호출하면 실제 위와 같이 구현된 동작을 수행하게 되는 것입니다.

참고 :
int exam::kuk_input(int pt)에서 ::문자를 Scope연산자라고 합니다. Scope연산자는 다양한 역활을 수행하는데 여기에서는 Class의 Member에 접근하도록 하는 역활을 수행하고 있습니다. 이때 Scope연산자는 exam의 kuk_input member 처럼 ~의 ~ 로 해석될 수 있습니다.

student.name_input("youngsoo");
 
  kukp = student.kuk_input(80);
  matp = student.mat_input(91);
  engp = student.eng_input(94);
 
student.memberprint();

Class의 함수를 호출합니다.

Class안의 각 함수는 Class안의 Member변수에 값을 설정할 수 있도록 구현되었습니다.(memberprint()함수는 값을 확인하도록 하였습니다.) 즉, Class안의 Member함수를 통해 Member변수에 접근하고 있는 것입니다.(이는 위에서 말씀드린 Scope연산자의 의해 가능해 집니다.)

이렇게 값을 설정하고 memberprint()함수를 호출하면 각 Member변수의 값을 확인할 수 있습니다.

위 Member함수에서 name이 아닌 kuk, mat, eng member변수를 설정하는 함수를 보면

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}


pt값이 90이상일때 3을 더하도록 구현되어 있음을 알 수 있습니다. 점수로 치면 90점 이상일때 가산점 3을 더하도록 하기 위한 의도입니다. 각 함수에서는 가산점이 붙은 값을 return하고 있으며 main()은 위 세개의 함수를 호출할때 계산된 가산점을 받도록 하고 있습니다.

kukp = student.kuk_input(80);
matp = student.mat_input(91);
engp = student.eng_input(94);

이처럼 Class의 Member함수도 일반 함수처럼 void로 할 수 있고 필요하다면 특정 값을 받도록 할 수도 있습니다. Class안의 함수라고 해서 일반함수와 특별히 다를 것은 없습니다.


만일 같은 Class에서 파생된 Object가 2개상이 있다면 상호간의 Object대입을 통해서 값을 초기화 할 수도 있습니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

main()
{
  exam student, student2;
 
  int kukp;
  int matp;
  int engp;
 
  student.name_input("youngsoo");
 
  kukp = student.kuk_input(80);
  matp = student.mat_input(91);
  engp = student.eng_input(94);
 
  student.memberprint();
  student2 = student;
  student2.memberprint();
 
  return 0;
}


exam Class로부터 student와 student2라는 Object를 생성하고 student object에서 값을 설정한 후 student2 Object에 student Object를 대입하였습니다.

student.memberprint();

이 후 student2 Object의 값을 확인해 보면 student Object와 값이 일치함을 알 수 있습니다.


위에서는 단일 Object를 설정하여 해당 Class에 값을 설정하였지만 Class를 통해 생성한 Object를 배열로 하면 해당 Class의 복사본을 배열길이만큼 확보할 수 있게 됩니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl << endl;
}

main()
{
  exam student[2];
 
  student[0].name_input("youngsoo");
  student[0].kuk_input(80);
  student[0].mat_input(91);
  student[0].eng_input(94);
 
  student[1].name_input("sooyoung");
  student[1].kuk_input(70);
  student[1].mat_input(81);
  student[1].eng_input(84);
 
  student[0].memberprint();
  student[1].memberprint();
 
  return 0;
}


exam student를 exam student[2]로 하여 2개의 배열길이만큼 Object를 생성합니다.

배열도 단일 Object와 똑같이 값을 설정하면 됩니다. [0]이나 [1]을 붙여 몇번째 배열에 Member를 호출하는지만 지정해 주면 되는 것입니다.


다만 일일이 첨자를 붙여 초기화 해주는 것이 불편하다면 문자배열을 초기화 하듯 {}를 써서 한꺼번에 초기화 할 수도 있습니다. 이는 생성자를 통해서 가능한데 자세한 내용은 생성자와 소멸자 부분을 참고해 주시기 바랍니다.

[Develop/C, C++] - [C, C++] Class구현시 생성자와 소멸자 활용

Class를 활용한 다음 Program을 봐주십시오.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
   student.memberprint();
 
  return 0;
}



위 Program에서 Class의 name과 kuk member에 접근하기 위해 Class내에서 memberprint()함수를 public으로 선언하였습니다.

따라서 이 함수외에 다른 외부함수에서는 name과 kuk member에 접근할 수 없게 되었습니다. 왜냐하면 name과 kuk member는 class안에서 private으로 선언되었기 때문입니다.

하지만 Program안에서 private Member에 접근할 수 있는 방법이 있는데 그것은 Class안에 friend함수를 선언하는 것입니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend void kukinout(exam ea);
};

위와 같이 Class안에서 friend함수를 선언하면 해당 함수는 private나 public의 적용을 받지 않습니다. 왜냐하면 friend함수는 Class의 Member가 아니기 때문입니다. 이것은 단지 Program내에서 kukinout함수가 있으면 이 함수안에서 만큼은 Class의 private Member에 접근을 허용한다라는 의미만 가지고 있을 뿐입니다. 즉, 외부함수와 Class간의 통로역활만 하고 있는 것입니다.

또한 friend함수에서 함수의 인수로 exam형의 인수(exam ea)를 지정하고 있습니다. 이는 Program안에서 kukinout함수를 작성할때 Class의 Member에 접근하기 위해 Class로 부터 파생된 Object를 전달해 줄 필요가 있기 때문입니다.

Class안에서 작성된 friend함수의 특성을 활용하기 위해서는 Program안에서 kukinout라는 똑같은 이름의 함수를 작성해야 합니다.

void kukinout(exam ea)
{
  ea.kuk = 20;
  cout << "국어점수 : " << ea.kuk << endl;
}

인수로 부터 전달받은 Object를 통해 Private Member인 kuk member의 값을 설정하고 그 값을 확인하고 있습니다.

kukinout함수는 Class가 아닌 Program안에서 쓰인 독자적 함수입니다. 따라서 함수의 원형도 선언할 수 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend void kukinout(exam ea);
};

void kukinout(exam ea);


함수의 원형을 Class밑에 선언하는 이유는 이 원형이 Class선언 이전에 위치하게 되면 exam이 정의되지 않았다는 오류를 일으키기 때문입니다.

이제 지금까지 말씀드린 모든 Code를 모두 조합하여 Program을 작성하면 다음과 같이 될 것입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend void kukinout(exam ea);
};

void kukinout(exam ea);
void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
  student.memberprint();
 
  kukinout(student);
 
  return 0;
}

void kukinout(exam ea)
{
  ea.kuk = 20;
  cout << "국어점수 : " << ea.kuk << endl;
}


kukinout(student); 로 kukinout함수를 호출합니다.


friend 함수는 Program안에서 쓰인 특정 함수에 대해서만 Class의 Member접근을 허용하는 것입니다. 하지만 Class자체를 friend화 하면 해당 Class전체에 대해서 Member접근을 허용할 수 있게 됩니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend class exam2;
};

class exam2 : public exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
};


위와 같이 Class자체를 friend로 선언하면 friend Class는 본래 Class의 모든 Member에 접근할 수 있게 됩니다. 단, Class exam2 : public exam 처럼 본래 Class로 부터 상속받는 Class인 경우에만 가능합니다.

Class의 상속에 대한 내용은 다음 글을 참고하여 주십시오.

[Develop/C, C++] - [C, C++] Class 상속

이렇게 되면 exam2 Class에서는 본래 Class Member를 원하는 대로 다룰 수 있게 됩니다.

void exam2::memberprint()
{
  kuk = 20;
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

exam2에서 exam Class의 kuk member에 접근하여 값을 설정하고 그 값을 확인하고 있습니다.(exam2 Class에서 Member변수는 name뿐입니다.)

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend class exam2;
};

class exam2 : public exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

void exam2::name_input(char *nm)
{
  strcpy(name, nm);
}

void exam2::memberprint()
{
  kuk = 20;
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
  exam2 student2;
 
  student.name_input("youngsoo", 90);
  student.memberprint();
 
  student2.name_input("sooyoung");
  student2.memberprint();
 
  return 0;
}



private는 Member로의 접근방지, public는 Member의 접근허용 용도로 사용됩니다. C++에서는 이와 더불어 protected라는 Keyword가 있습니다. 이것은 일반적으로는 private의 성격을 가지고 있으나 다른 Class가 해당 Class로터 상속될때에는 public성격을 가지는 특징이 있습니다.

Class의 Public는 다음과 같이 Program내에서 접근이 가능하지만

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
  student.memberprint();

  return 0;
}



이것을 protected로 고치면 상황은 달라집니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  protected:
    void name_input(char *nm, int i);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
  student.memberprint();

  return 0;
}



오류발생(접근할 수 없음) Error가 출력되는데 이는 protected Member로의 접근이 오로지 상속된 Class에서만 가능하기 때문입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  protected:
    void name_input(char *nm, int i);
    void memberprint();
};

class exam2 : public exam{
  public:
    void input();
    void print();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

void exam2::input()
{
  name_input("youngsoo", 90);
}

void exam2::print()
{
  memberprint();
}

main()
{
  exam2 student;
 
  student.input();
  student.print();
 
  return 0;
}


exam Class로 부터 상속하여 exam2 Class를 생성합니다.

상속된 Class는 기존 Class에서 protected된 Member에 다음의 형태처럼 접근하는 것이 가능해 집니다.

void exam2::input()
{
  name_input("youngsoo", 90);
}

void exam2::print()
{
  memberprint();
}


상속된 exam2 Class에서 exam Class의 protected Member인 name_input과 memprint Member에 접근하고 있습니다.


위의 friend설명부분에서 쓰인 memberprint()함수를 다시 한번 살펴보겠습니다.

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

이 함수에서는 exam Class의 name, kuk, mat, eng member변수의 값을 확인하도록 하고 있습니다. 그런데 만일 이 함수안에 어떠한 이유로 인하여 kuk이라는 변수가 새로 선언되었을때 결과는 어떻게 될까요?

void exam::memberprint()
{
  int kuk = 0;
 
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

memberprint()함수안에 kuk이라는 변수를 선언하고 초기값을 0으로 합니다.


Program실행결과가 0으로 나왔습니다. 이는 Class의 kuk Member가 아니라 함수안에서 쓰인 kuk변수값을 가져오고 있기 때문입니다.

이렇게 혼동되는 문제를 해결하는 방법에는 두가지가 있는데 하나는 해당 Member에 Class를 지정해 주는 것이며

cout << "국어점수 : " << exam::kuk << endl;

다른 하나는 This Pointer를 지정하는 것입니다.

cout << "국어점수 : " << this->kuk << endl;

이 This Pointer는 이름에서도 알 수 있듯이 주소값을 갖는 Pointer로서 해당 주소는 다름아닌 Class에서 생성된 Object의 주소를 갖고 있는 것입니다.

결국

exam student;

student.memberprint();


이와같이 하였을때 This에는 student Object의 주소가 담겨져 전달되기 때문에 Class의 kuk Member값을 가져오게 됩니다.

방금전 This Pointer를 사용해 보았는데 이것으로 알 수 있는것은 Class의 Member에 접근하는데 Object를 통해서만 아니라 Pointer를 통해 접근할 수도 있다는 것입니다.

아래 예제는 위에서 다룬 Program에서 점수부분은 빼고 이름만 설정/확인할 수 있도록 간소화한 예제입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
}

main()
{
  exam student;
 
  exam *p = &student;
 
  student.name_input("youngsoo");
  student.memberprint();
 
  p->memberprint();
 
  return 0;
}


student의 Object주소값을 exam형 Class의 Pointer에 전달하고 이 Pointer를 통하여 Class의 memberprint() Member함수에 접근하고 있습니다.

Object의 주소값을 Pointer에 넘기려면 해당 Pointer가 Object가 생성된 Class형으로 선언되어야 하며 이 Pointer에 & 연산자를 통하여 Object의 주소값만 넘겨주기만 하면 됩니다.

이 후 Object의 주소를 전달받은 Pointer는 -> 연산자를 통해 Class의 Member에 접근하도록 합니다.


Class로부터 생성된 Object를 Const로 선언하면 해당 Object에서는 각 Member에 접근할 수 없는 상황이 발생합니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  const exam student;

  student.name_input("youngsoo", 90);
  student.memberprint();
 
  return 0;
}


exam Class의 student Object를 const로 선언하였습니다.


잘 아시는 바와 같이 const는 상수입니다. 즉, 값을 바꿀 수 없는 것이기 때문에 Member접근 자체가 차단되는 것입니다.

값을 설정하는 name_input member함수는 그렇다 치고 단순히 값을 조회하는 memberprint()함수까지 차단되는 이유는 뭘까요? 이는 C++ Compiler가 어떤 함수에서 값을 변경하고 또 어떤 함수에서 값을 변경하지 않는지까지는 판단하지 않기 때문입니다.

즉, 값을 변경하는 부분과 값을 변경하지 않는 부분을 구분하지 않기 때문에 아예 모두 틀어막아 버릴 수 밖에 없게 되는 것이죠.

이렇게 되면 값을 확인할 수도 없어서 const로 설정하는 것 자체가 거의 무의하게 되지만 값을 바꾸지 않는 Class의 Member함수에 한하여 const로 선언하게 되면 해당 함수는 사용을 가능하게 할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    exam(char *nm, int i);
    void memberprint() const;
};

Class에서 memberprint() Member함수에 const선언하였습니다. memberprint()는 값을 설정하는 부분이 없고 오로지 값을 확인하는 부분만 존재합니다.

또한 해당 Class의 각 Member에 초기값을 설정하기 위해 exam생성자를 선언하였습니다. 만일 생성자가 아닌 기존에서 처럼

void name_input(char *nm, int i);

하여 Member함수로 값을 설정하려고 하면 해당 Object생성시 const로 했을때 값을 설정하는 Member로의 접근이 차단 되므로 초기값을 설정하는 것 자체가 불가능하게 됩니다.

생성자에 대해서는 다음글을 참고하여 주십시오.

[Develop/C, C++] - [C, C++] Class구현시 생성자와 소멸자 활용

Class에서 memberprint()함수에 const를 선언하고 나면 해당 함수의 정의부분에서도 const를 추가해야 합니다.
void exam::memberprint() const
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

함수 정의 마지막 부분에 const구문을 추가하였습니다.

만일 Class안에서 선언과 정의가 동시에 이루어 지는 경우라면 다음과 작성할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    exam(char *nm, int i);
    void memberprint() const {
      cout << "학생이름 : " << name << endl;
      cout << "국어점수 : " << kuk << endl;
    }
};

마지막으로 선언된 생성자에 대한 정의를 작성한 후

exam::exam(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

초기화를 수행하는 부분을 추가하면 다음과 같이 될것입니다.

main()
{
  const exam student("youngsoo", 90);
  student.memberprint();
 
  return 0;
}

student Object생성과 동시에 초기화 합니다. 이때 const선언도 같이 이루어 지도록 합니다.

위와 같은 내용으로 Program을 완성시켜 보겠습니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    exam(char *nm, int i);
    void memberprint() const;
};

exam::exam(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint() const
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  const exam student("youngsoo", 90);
  student.memberprint();
 
  return 0;
}


const에 대한 Member함수 접근


memberprint()는 const선언되었으므로 정상적인 처리 가능

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

표준 Library 함수 - Memory 관련 함수  (0) 2011.06.16
[C, C++] Class구현시 생성자와 소멸자 활용  (0) 2011.06.01
[C, C++] Class  (0) 2011.05.25
[C, C++] cout 서식지정  (0) 2011.04.14
[C, C++] 구조체  (1) 2011.04.13
[C, C++] 변수선언및 초기화  (0) 2011.04.07
0 0
Programming/C C++
1. 진수표시

C++에서는 어떠한 Data를 원하는 진수형식으로 표현하기 위해 oct(8진수), dec(10진수), hex(16진수) 등을 사용합니다.(이런것을 흔히 Manipulator이라고 합니다.)

#include <iostream.h>

main()
{
  int i;
 
  i = 12;
 
  cout << "12 -> 8진수 : " << oct << i << endl;
  cout << "12 -> 10진수 : " << dec << i << endl;
  cout << "12 -> 16진수 : " << hex << i << endl;
 
  return 0;
}


oct << i, dec << i, hex << i를 통해 12 i값을 각 진수로 변환하고 있습니다.


이외에도 endl처럼 줄바꿈을 하는 ends(\0 처리), ws(입력시 공백제외)등 여러가지가 있습니다.

2. 자리수 지정

출력되는 Data에 일정한 간격을 표시하거나 소수출력시 자리수를 맞추기 위한 함수로 C++에는 setfill(), setw(), setprecision() 함수가 있습니다.(이 세개의 함수는 iomanip.h Header File에 정의되어 있습니다.)

#include <iostream.h>
#include <iomanip.h>

main()
{
  cout << setw(15) << "hello!" << endl;
 
  return 0;
}


setw()함수를 사용하여 출력되는 전체자리수를 지정하고 있습니다.


#include <iostream.h>
#include <iomanip.h>

main()
{
  cout << setw(15) << setfill('-') << "hello!" << endl;
 
  return 0;
}


setfill()함수로 출력되고 난 후의 빈공간을 - 문자로 채우도록 합니다.


#include <iostream.h>
#include <iomanip.h>

main()
{
  double d;
 
  d = 12.345;
 
  cout << setprecision(3) << d << endl;
 
  return 0;
}


setpecision()함수로 12.345를 3자리로 맞추어 출력하도록 합니다.

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

[C, C++] Class구현시 생성자와 소멸자 활용  (0) 2011.06.01
[C, C++] Class  (0) 2011.05.25
[C, C++] cout 서식지정  (0) 2011.04.14
[C, C++] 구조체  (1) 2011.04.13
[C, C++] 변수선언및 초기화  (0) 2011.04.07
[C, C++] C에서 C++로의 변화  (1) 2011.04.06
0 0
1 2 3
블로그 이미지

클리엘