C++

지금까지는 하나의 클래스에 대해 이야기를 했습니다. 지금부터는 하나 이상의 클래스를 이야기할까 합니다. 즉, 클래스 간의 관계에 대한 이야기입니다.

 

클래스의 관계는 여러 가지가 있지만 재사용 측면에서 크게 두 가지 관계가 존재합니다.( 우리는 복잡한 것을 생각하지 않으므로 간단하게 접근합니다.)

  • 첫째, '-이다'의 관계( is_a 관계)

    • 상속(inheritance)
  • 둘째, '-가지다'의 관계( has_a 관계)

    • 포함(composition)

 

첫째, is_a 관계는 Person 클래스와 Student 클래스의 관계입니다.

  • Student(학생)은 Person(사람) 이다. 관계이기 때문에..
  • 그림으로는 아래와 같이 표현할 수 있습니다.

상속Person.png 

이때 Person 클래스를 부모(Parent) 클래스라 하고 Student, Professor 클래스를 자식(child) 클래스라 합니다.

 

 

둘째, has_a 관계는 Car 클래스와 Radio 클래스의 관계입니다.(이것은 다음에 진행합니다. --;)

  • Car(자동차)는 Radio(라디오)를 가진다. 관계이기 때문에..
  • 그림으로는 아래와 같이 표현할 수 있습니다.

포함car.png 

 

 1, 상속

상속은 부모 클래스의 모든 내용(속성과 행동)을 자식 클래스가 물려 받는 것입니다.

위 그림에서 Person의 속성(멤버 변수)와 행동(멤버 함수)을 Student가 물려 받습니다. 그리고 자신만의 상태와 행동을 추가하여 내용을 확장합니다.

그래서 상속을 이용하면 코드를 재상용하여 효율적으로 클래스를 생성할 수 있습니다.

 

Student 클래스를 만들기 위한 간단한 예제코드를 볼까요?

첫째, 상속을 사용하지 않는 Student 클래스 정의

class Student
{
    char name[20];
    int age;
    int grade;
public:
    void Eat( ) {   }
    void Study( ) { }
};

void main( )
{
    Student std1;
}

 학생은 사람이므로 사람으로서 필요한 모든 내용(속성과 행동)을 Student 클래스가 정의하고 있습니다. 이렇게 되면 Professor 클래스를 정의할 때도 사람에 필요한 모든 내용을 정의해야 합니다. 그러면 코드 중복이 발생하여 여러 가지 불이익을 받게 됩니다.

 

Student 객체의 그림입니다.

상속없는_student_객체.png 

 

 

둘째, 상속을 사용한 Student 클래스 정의

class Person
{
    char name[20];
    int age;
public:
    void Eat( ) {   }
};
class Student : public Person
{
    int grade;
public:
    void Study( ) { }
};

void main( )
{

    Person person1;

 

    Student student1;

}

여기서 Person 클래스가 이미 존재한다고 가정한다면 Student 클래스를 정의하는 것은 위쪽 예제보다 훨씬 간단합니다. 속성 grade와 행동 Sudent()만 정의할 뿐이니까요. 그리고 부가적으로 따라오는 이득도 만만치 않습니다. 문법적으로 상속은 " : public Person"와 같이 합니다.(빨간색 부분)

 

Person 객체와 Student 객체의 그림입니다.

당연한 이야기지만 Student 객체는 위쪽 상속을 사용하지 않는 객체의 속성, 행동과 같은 객체가 만들어 집니다. 상속을 사용해서 Student 클래스를 정의한 것뿐이니까요.

상속없는_student_객체(2).png 

 

 

2, 함수 재정의(function overriding)

 함수 재정의란 부모 클래스에 정의된 멤버 함수를 자식 클래스에서 다시 정의하는 것을 말합니다.

 

예로 아래와 같은 Person 클래스가 있다고 가정합니다.

#include <iostream>
using namespace std;

class Person
{
    char name[20];
    int age;
public:
    Person(const char* n, int a)
    {
        strcpy(name, n);
        age = a;
    }
    void Eat( ) {   }
    void Print( )
    {
        cout << "name : " << name <<", " <<"age : " << age << endl;
    }
};
void main( )
{
    Person person1("김영수", 20);

    person1.Print( );
}
  1. name : 김영수, age : 20

 person1의 Print() 멤버 함수는 이름과 나이를 출력합니다.

 

이때 학생을 정의하기 위한 Student 클래스가 필요합니다.

이미 Person 클래스가 존재하므로 Student 클래스는 Person을 상속받아 만들기로 결정합니다.

아래와 같이..

#include <iostream>
using namespace std;

class Person
{
    char name[20];
    int age;
public:
    Person(const char* n, int a)
    {
        strcpy(name, n);
        age = a;
    }
    void Eat( ) {   }
    void Print( )
    {
        cout << "name : " << name <<", " <<"age : " << age << endl;
    }
};
class Student : public Person
{
    int grade;
public:
    Student(const char* n, int a, int g):Person(n,a), grade(g)     {
    }
    void Study( ) { }
};
void main( )
{
    Student student1("김학생", 20, 1);

    student1.Print( );
}
  1. name : 김학생, age : 20

 그리고 학생 객체 student1을 만들어 학생의 정보를 출력(Print() 호출)하지만 Print() 함수는 grade의 정보를 출력하지 못합니다.

Print()함수는 Person의 메소드로 grade의 어떠한 내용도 알지 못하기 때문입니다.

한마디로 Person 클래스보다 Student 클래스가 더 구체화된 클래스이므로 Print() 함수의 기능도 Student에 맞게 더 구체화 시켜야 합니다.

그래서 부모 클래스(Person)의 함수(Print())를 자식 클래스(Student)에서 재정의하는 것입니다.

아래 예제와 같이...

#include <iostream>
using namespace std;

class Person
{
    char name[20];
    int age;
public:
    Person(const char* n, int a)
    {
        strcpy(name, n);
        age = a;
    }
    void Eat( ) {   }
    void Print( )
    {
        cout << "name : " << name <<", " <<"age : " << age << endl;
    }
};
class Student : public Person
{
    int grade;
public:
    Student(const char* n, int a, int g):Person(n,a), grade(g)     {
    }
    void Study( ) { }
    void Print( ) // 함수 재정의
    {
        Person::Print(); // 이름과 나이를 출력하고
        cout << "grade : " << grade << endl; // 학년도 출력합니다.
    }
};
void main( )
{
    Student student1("김학생", 20, 1);

    student1.Print( );
}
  1. name : 김학생, age : 20
    grade : 1

 Student 클래스에서 Print()함수를 재정의하여 이름과 나이도 출력하고(Person::Print()) 학년도 출력합니다. 재정의 함수에서 부모의 함수를 호출하려면 접근 연산자(::)를 사용해야 합니다. ( Person::Print() <=  이렇게... )

아니면 재귀함수 호출이 되겠죠?

 
출처 : http://coolprogramming.springnote.com/pages/3422007

C언어는 날짜/시간을 구할 때 하나의 함수로만 되는 것이 아니라, 다음과 같이 약간 복잡합니다.

time() 함수로, 현재 경과된 초(sec), 즉 "유닉스 시간"을 구한 후, 그것을 localtime() 함수로 연월일 시분초로 분리하여 구조체에 저장합니다.

C에서, 오늘 시각/날짜 (현재 날짜, 시간) 출력 예제


파일명: 0.cpp
#include <stdio.h>
#include <time.h>


void main(void) {
  time_t timer;
  struct tm *t;

  timer = time(NULL); // 현재 시각을 초 단위로 얻기

  t = localtime(&timer); // 초 단위의 시간을 분리하여 구조체에 넣기


  printf("유닉스 타임 (Unix Time): %d 초\n\n", timer); // 1970년 1월 1일 0시 0분 0초부터 시작하여 현재까지의 초

  printf("현재 년: %d\n",   t->tm_year + 1900);
  printf("현재 월: %d\n",   t->tm_mon + 1);
  printf("현재 일: %d\n\n", t->tm_mday);

  printf("현재 시: %d\n",   t->tm_hour);
  printf("현재 분: %d\n",   t->tm_min);
  printf("현재 초: %d\n\n", t->tm_sec);

  printf("현재 요일: %d\n", t->tm_wday); // 일요일=0, 월요일=1, 화요일=2, 수요일=3, 목요일=4, 금요일=5, 토요일=6
  printf("올해 몇 번째 날: %d\n", t->tm_yday); // 1월 1일은 0, 1월 2일은 1
  printf("서머타임 적용 여부: %d\n", t->tm_isdst); // 0 이면 서머타임 없음

}



실행 결과 화면:
D:\Z>cl 0.cpp && 0.exe
0.cpp
유닉스 타임 (Unix Time): 1160032094 초

현재 년: 2006
현재 월: 10
현재 일: 5

현재 시: 16
현재 분: 8
현재 초: 14

현재 요일: 4
올해 몇 번째 날: 277
서머타임 적용 여부: 0
D:\Z>



time.h 에 시간 구조체가 다음과 같이 정의되어 있습니다.

struct tm {
  int tm_sec;   /* Seconds */
  int tm_min;   /* Minutes */
  int tm_hour;  /* Hour (0--23) */
  int tm_mday;  /* Day of month (1--31) */
  int tm_mon;   /* Month (0--11) */
  int tm_year;  /* Year (calendar year minus 1900) */
  int tm_wday;  /* Weekday (0--6; Sunday = 0) */
  int tm_yday;  /* Day of year (0--365) */
  int tm_isdst; /* 0 if daylight savings time is not in effect) */

};




업데이트: 비주얼C 2005 이상에 최적화: ▶▶ C언어] localtime_s 함수 사용법: 비주얼 Visual C 2005 이상에서

출처 : http://mwultong.blogspot.com/2006/10/c-current-date-time.html
출처 삶 그리고 깨달음 | 프리맨
원문 http://blog.naver.com/lonekid/60008258279

알다시피 실수를 표현하는 방법에는 부동소수점과 고정소수점 두 가지가 있다.

부동소수점은 일반적으로 C에서 float 이나 double 형으로 표현되는 방식이다. 이에 비해 고정소수점은 정수부와 소수부에 고정 비트수를 할당하여 [n.0 ~ n+1.0) 의 범위를 표현하는 방식이다. 즉 지수승은 표현하지 못하며 소수부에 대해 [0 ~ 1) 의 범위를 표현하는 방식이다. 이런 연유로 인해 고정소수점은 일반적으로 정수와 동일한 비트 표현을 갖기 때문에 정수연산과 동일한 레벨에서 처리할 수 있다.


아무리 FPU가 탑재되어 있다하더라도 정수 연산에 비해 부동소수점 연산은 수십배 이상 느리다. 그렇기 때문에 지수승에 의한 넓은 범위의 수치를 다루는 경우가 아니라면 고정소수점을 사용하는 것이 충분한 가치를 갖는다. 일반적으로 학문적 수준에서의 알고리즘들은 실수 구간의 연속 데이타를 대상으로 하지만 컴퓨터는 기본적으로 양자화된 이산(정수) 데이타를 대상으로 한다. 즉, 컴퓨터에 있어 알고리즘의 최종 결과는 대부분 정수이다. 그렇기 때문에서 알고리즘을 컴퓨터로 구현함에 있어 실수가 아닌 정수를 대상으로 하는 고속화된 이산(정수) 알고리즘들이 고안되는 것이다.


일반적으로 실수 알고리즘들은 [0 ~ 1) 범위의 노말라이즈된 수치로 표현되는 것이 보통이다.

정수부와 소수부에 각각 16비트씩 할당하는 16:16 고정소수점을 생각해보자. 정수부는 -32768 ~ +32767의 범위를 표현할 수 있으며 소수부는 [0 ~ 1) 의 범위를 65536 단계의 해상도(소수점 이하 4~5자리의 정확도)로 표현하게 된다. 즉, 0.1은 6554(65536*0.1), 0.5는 32768(65536*0.5), 0.8은 52429(65536*0.8)로 표현된다. 이렇게 표현되는 고정소수점에 대한 가감승제 연산은 컴퓨터 입장에서 32비트 정수에 대한 가감승제 연산과 동일하다. 다만 16비트 소수부에 대한 자리수만 고려해 주면 된다.


이렇게 표현되는 고정소수점에 대한 수학 클래스를 예시한다.

이 클래스에서는 고정소수점 버전의 삼각함수(Sin, Cos, Tan)를 테이블 방식으로 구현하고 있는데 이해하는데 어려움은 없을 것이다.


김인대



// 16:16 fixed point constant

#define FIXED_PI2       102944      // (3.1415926535 / 2) * 65536

#define FIXED_2PI       411775      // (2 * 3.1415926535) * 65536

#define FIXED_PI        205887      // 3.1415926535 * 65536

#define FIXED_R2D       3754936     // (180 / 3.1415926535) * 65536

#define FIXED_D2R       1144        // (3.1415926535 / 180) * 65536

#define FX_R2D(fxR)     ::MulDiv((fxR), 180 * 65536, FIXED_PI)

#define FX_D2R(fxD)     ::MulDiv((fxD), FIXED_PI, 180 * 65536)

 

#ifndef _countof

#define _countof(a)     (sizeof(a)/sizeof(a[0]))

#endif

 

// 16:16 fixed point

typedef long CFixed;

 

// 16:16 fixed point math class.

class CFxMath

{

public:

    static int ToInt(CFixed fxDeg)      { return fxDeg >> 16; }

    static int Round(CFixed fxDeg)      { return (fxDeg + 32768) >> 16; }

    static double ToDbl(CFixed fxDeg)   { return fxDeg / 65536.0; }

    static CFixed ToFx(int n)           { return n << 16; }

    static CFixed ToFx(double d)        { return CFixed(d * 65536); }

   

    static CFixed FxSin(CFixed fxDeg);

    static CFixed FxCos(CFixed fxDeg)   { return FxSin(fxDeg + 90 * 65536); }   // sin(deg + pi/2)

    static CFixed FxTan(CFixed fxDeg);

 

private:

    enum

    {

        eSinTableResolution = 7     // The resolution(bits) of fraction part.

    };

    static USHORT m_awSinTable[90 * (1 << eSinTableResolution)];    // 0 ~ 89.n degree

    static bool m_bInitSinTable;

    static bool InitSinTable();

};

 

// static

CFixed CFxMath::FxSin(CFixed fxDeg)

{

    // Normalize to -360 ~ +360 degree.

    if (fxDeg <= -360 * 65536 || 360 * 65536 <= fxDeg)

    {

        fxDeg = fxDeg % (360 * 65536);

    }

   

    // Normalize to 0 ~ 360 degree.

    if (fxDeg < 0)

    {

        fxDeg = (360 * 65536) + fxDeg;  // sin(2*pi + n) == sin(n)

    }

   

    // Adjust angle for quadrant.

    int nQuad = fxDeg / (90 * 65536);

    switch (nQuad)

    {

    case 1:     // 90 ~ 180 degree

        fxDeg = (180 * 65536) - fxDeg;

        break;

    case 2:     // 180 ~ 270 degree

        fxDeg = fxDeg - (180 * 65536);  // -sin(fxDeg)

        break;

    case 3:     // 270 ~ 360 degree

        fxDeg = (360 * 65536) - fxDeg;  // -sin(fxDeg)

        break;

    case 0:     // 0 ~ 90 degree

    default:

        fxDeg;

        break;

    }

    ASSERT(0 <= fxDeg && fxDeg <= (90 * 65536));

   

    // Sin table lookup.

    long fxSinValue;

    if (fxDeg == (90 * 65536))

    {

        fxSinValue = 1 * 65536;             // For 90 degree

    }

    else

    {

        int nDegIdx = fxDeg >> (16 - eSinTableResolution);

        fxSinValue = m_awSinTable[nDegIdx]; // For 0 ~ 89.n degree

    }

    return (nQuad < 2) ? fxSinValue : -fxSinValue;  // 16:16 fixed point

}

 

// static

CFixed CFxMath::FxTan(CFixed fxDeg)

{

    CFixed fxCos = FxCos(fxDeg);

    return (fxCos == 0) ? MAXLONG : ::MulDiv(FxSin(fxDeg), 65536, fxCos);

}

 

USHORT CFxMath::m_awSinTable[90 * (1 << eSinTableResolution)];  // 0 ~ 89.n degree

bool CFxMath::m_bInitSinTable = InitSinTable();

 

bool CFxMath::InitSinTable()

{

    const double d2r = 3.1415926535 / 180;

    for (int nDegIdx = 0; nDegIdx < _countof(m_awSinTable); nDegIdx++)

    {

        double dDeg = (double)nDegIdx / (1 << eSinTableResolution);

        double dSin = ::sin(dDeg * d2r);

        m_awSinTable[nDegIdx] = USHORT(dSin * 65536 + 0.5);

    }

    return true;

}

'C/C++언어' 카테고리의 다른 글

고정 소수점 (C++언어 버젼)  (0) 2008.05.09
코드 최적화  (0) 2008.05.09
[알고리즘]고정소수점(fixed point) 연산  (0) 2008.05.09
음 고정소수점 만들기..  (0) 2008.05.09
고정 소수점  (0) 2008.05.09
각 언어별 3중 포인터  (0) 2008.05.06
C++

정녕~ 이럴수가.. 회사에서 printf 를 만들라는 과제를 받아 만들었습니다.

이사님께 튜트리얼 을 좀 배우고 나서 만들어 본 것인데...

정녕 이걸 네이버에 주소값을 검색해보니.. 개털~ 안나오더군요 google 엔 양키들이 한것이 조금 있더군요


한국 사람들이 얼마나 쪼잔한지 알것 같습니다 .ㅋㅋㅋㅋ

하지만 전 과감하게 올립니다!!

구글에 검색했을때 0xB0008000 을 하니 나왔습니다! 바로 검색어가 0xB0008000 이었는데..

ㅡ,ㅡ.. 문제는 VC 에서는 안됩니다.. 그레서 볼랜드 C 에서 햇습니다.

컴파일 옵션에서 메모리 할당을 large 로 바꿔야 돌아갑니다..


자 BC 로 옵션을 마추어 놓고~ 시작을 합니다. 그전에 알아야 할 것들이 0xB0008000 은 바로 텍스트 모드의 메모리 의 첫 주소 입니다.

0xB0008000 은 콘솔창( VC 로 콘솔 모드로 만들면 뜨는 cmd 창 ) 의 잴 왼쪽 위 구석탱이를 가르킵니다. 그 다음은 2바이트씩

0xB0008002 는 2번째를 가르키겠죠. 그곳에 아스키 코드를 넣어주면 출력이 됩니다. 간단하죠?

하지만 왜 2바이트 단위냐~ 바로 첫번째는 글씨( 0xB0008000 ) 이고 두번째는 ( 0xB0008001 ) 은 색입니다.

XRGB 로 4비트로 구성이 되어 있습니다.

X 는밝기

R 빨간

G 초록

B 파란


이런 식 입니다.

그렇다면 맨 왼쪽위에 A 를 출력하고 싶다! 그렇다면


char * p = (char*)0xB0008000;

*p = 0x41;

*(p+1) = 0x04;


자 빨간색 A 가 나옵니다 . 왜 0x04 가 빨간색이냐!?


X R G B

0 0 0 0

검은색입니다.

X R G B

0 1  0 0

빨간색입니다.

비트를 계산하는 방식이

‥‥ 32 16 8 4 2 1

이죠.. 이걸 매치시킨다면!?


X R G B

8 4 2 1

이가 되겠죠. 그렇다면 빨간색은 4, 그럼 파란색은 1, 초록은 2, 하얀색은 모두더한 F 가 되겟죠..참고로 C 언어는 16진수로 인식을 합니다~ 주의하셔야 합니다.


자 그렇다면 가로가 80입니다. 세로는 24이지요. 그럼 다음번째 줄에 A 를 쓰려면

*p+80 을 하면 됩니다. 그럼 다음번째 줄이 되겟죠..

자 아주 간단하지요?

회사 강의 란에 printx 와 putchx 가 있을것 입니다. 제가 만들었죠 훗..


아레는 BC 의 옵션 설정 방법 입니다.

 

사용자 삽입 이미지

사용자 삽입 이미지

그리고 요번엔 예제 실행 파일 입니다.

사용자 삽입 이미지
사용자 삽입 이미지


소스 파일은


#include <conio.h>
 //4 2 1
enum Color{
 C_RED = 0x04,
 C_GREEN = 0x02,
 C_BLUE = 0x01,
 C_BLACK = 0x00,
 C_WHITE = 0x0F,
 C_GRAY = 0x07
 };

void putchxy( int x, int y, char Text, Color eText )
{
 char * pAddr = (char *)0xB0008000;
 pAddr = pAddr + ( ( y * 160 ) + x * 2 );
 *pAddr = Text;
 *(pAddr+1) = eText;
}

void printxy( int x, int y, char * pText, Color eText )
{
 int iOffset = 0;
 while( *pText != 0 )

 {
  putchxy( x + iOffset, y, *pText, eText );
  ++iOffset;
  ++pText;
 }
}

int main()
{
 printxy( 10, 10, "출력하는 예제입니다.", C_RED );
 printxy( 10, 11, "미희야 사랑해", C_WHITE );
 getch();
 return 0;
}


출처 : http://blog.naver.com/312160/150023748818

'C/C++언어' 카테고리의 다른 글

템플릿 사용한 max 만들기  (0) 2008.03.03
<이클립스 디버그 방법>  (0) 2008.01.28
printf 문을 만들어보자  (0) 2008.01.28
여러 C++ 컴파일러  (0) 2007.09.06
함수포인터란 ?  (0) 2007.09.06
[본문스크랩] 파일 입.출력  (0) 2007.09.06

+ Recent posts