'전체 글'에 해당되는 글 678건

  1. 2011.08.13 Chinese Character Codes
  2. 2011.08.13 CDatabase 을 사용한 엑셀 데이터 수정
  3. 2011.08.13 cast 연산자
  4. 2011.08.13 c++ 추상 기초클래스

http://www.khngai.com/chinese/charmap/

 

 

GB 2312 Character Map, 0xA1A0 - 0xA9FF

  0 1 2 3 4 5 6 7 8 9 a b c d e f
A1A0     · ˉ ˇ ¨
A1B0
A1C0 ± × ÷
A1D0
A1E0 ° ¤ §
A1F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A2A0                                
A2B0  
A2C0
A2D0
A2E0      
A2F0        
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A3A0  
A3B0
A3C0
A3D0 _
A3E0
A3F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A4A0  
A4B0
A4C0
A4D0
A4E0
A4F0                        
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A5A0  
A5B0
A5C0
A5D0
A5E0
A5F0                  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A6A0   Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο
A6B0 Π Ρ Σ Τ Υ Φ Χ Ψ Ω              
A6C0   α β γ δ ε ζ η θ ι κ λ μ ν ξ ο
A6D0 π ρ σ τ υ φ χ ψ ω              
A6E0                                
A6F0                                
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A7A0   А Б В Г Д Е Ё Ж З И Й К Л М Н
A7B0 О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э
A7C0 Ю Я                            
A7D0   а б в г д е ё ж з и й к л м н
A7E0 о п р с т у ф х ц ч ш щ ъ ы ь э
A7F0 ю я                            
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A8A0   ā á ǎ à ē é ě è ī í ǐ ì ō ó ǒ
A8B0 ò ū ú ǔ ù ǖ ǘ ǚ ǜ ü ê          
A8C0          
A8D0
A8E0            
A8F0                                
  0 1 2 3 4 5 6 7 8 9 a b c d e f
A9A0        
A9B0
A9C0
A9D0
A9E0
A9F0                                

 

GB 2312 Character Map, 0xB0A0 - 0xBFFF

  0 1 2 3 4 5 6 7 8 9 a b c d e f
B0A0  
B0B0
B0C0
B0D0
B0E0
B0F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B1A0  
B1B0
B1C0
B1D0
B1E0 便
B1F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B2A0  
B2B0 簿
B2C0
B2D0
B2E0
B2F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B3A0  
B3B0
B3C0
B3D0 齿
B3E0
B3F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B4A0   穿
B4B0 椿
B4C0
B4D0 蹿
B4E0
B4F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B5A0  
B5B0
B5C0
B5D0
B5E0 殿
B5F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B6A0  
B6B0
B6C0
B6D0
B6E0
B6F0 饿  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B7A0  
B7B0
B7C0 仿 访
B7D0 忿
B7E0
B7F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B8A0  
B8B0
B8C0 竿
B8D0
B8E0 稿
B8F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
B9A0  
B9B0
B9C0
B9D0
B9E0 广
B9F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
BAA0  
BAB0
BAC0
BAD0
BAE0 鸿
BAF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
BBA0  
BBB0 怀
BBC0
BBD0 贿
BBE0
BBF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
BCA0  
BCB0
BCC0
BCD0
BCE0
BCF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
BDA0  
BDB0
BDC0 轿
BDD0
BDE0
BDF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
BEA0  
BEB0
BEC0
BED0
BEE0
BEF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
BFA0  
BFB0
BFC0
BFD0
BFE0
BFF0 岿  

 

GB 2312 Character Map, 0xC0A0 - 0xCFFF

  0 1 2 3 4 5 6 7 8 9 a b c d e f
C0A0  
C0B0
C0C0
C0D0
C0E0
C0F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C1A0  
C1B0
C1C0
C1D0
C1E0
C1F0 窿  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C2A0  
C2B0 鹿
C2C0 绿
C2D0
C2E0
C2F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C3A0  
C3B0
C3C0
C3D0
C3E0
C3F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C4A0  
C4B0
C4C0
C4D0
C4E0
C4F0 尿  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C5A0  
C5B0
C5C0
C5D0
C5E0
C5F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C6A0  
C6B0
C6C0
C6D0
C6E0
C6F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C7A0  
C7B0
C7C0
C7D0
C7E0
C7F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C8A0  
C8B0
C8C0
C8D0
C8E0
C8F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
C9A0  
C9B0
C9C0
C9D0
C9E0
C9F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
CAA0   湿
CAB0 使
CAC0
CAD0 寿
CAE0
CAF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
CBA0  
CBB0
CBC0
CBD0 宿
CBE0
CBF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
CCA0  
CCB0
CCC0
CCD0
CCE0
CCF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
CDA0  
CDB0
CDC0 退
CDD0
CDE0
CDF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
CEA0  
CEB0
CEC0
CED0
CEE0
CEF0 西  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
CFA0  
CFB0
CFC0
CFD0 线
CFE0
CFF0  

 

GB 2312 Character Map, 0xD0A0 - 0xDFFF

  0 1 2 3 4 5 6 7 8 9 a b c d e f
D0A0  
D0B0
D0C0
D0D0
D0E0
D0F0 婿  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D1A0  
D1B0
D1C0
D1D0 沿
D1E0
D1F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D2A0   耀
D2B0
D2C0
D2D0 亿
D2E0
D2F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D3A0  
D3B0
D3C0
D3D0
D3E0 屿
D3F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D4A0  
D4B0
D4C0
D4D0
D4E0
D4F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D5A0  
D5B0
D5C0
D5D0
D5E0
D5F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D6A0  
D6B0
D6C0
D6D0
D6E0
D6F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D7A0  
D7B0
D7C0 姿
D7D0
D7E0
D7F0            
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D8A0   廿 丿
D8B0
D8C0
D8D0
D8E0
D8F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
D9A0  
D9B0
D9C0
D9D0
D9E0
D9F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
DAA0  
DAB0
DAC0 诿
DAD0
DAE0
DAF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
DBA0  
DBB0
DBC0
DBD0
DBE0
DBF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
DCA0  
DCB0
DCC0
DCD0
DCE0
DCF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
DDA0  
DDB0
DDC0
DDD0
DDE0
DDF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
DEA0  
DEB0
DEC0
DED0
DEE0
DEF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
DFA0  
DFB0
DFC0
DFD0
DFE0
DFF0  

 

GB 2312 Character Map, 0xE0A0 - 0xEFFF

  0 1 2 3 4 5 6 7 8 9 a b c d e f
E0A0  
E0B0
E0C0
E0D0
E0E0
E0F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E1A0  
E1B0
E1C0
E1D0
E1E0
E1F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E2A0  
E2B0
E2C0
E2D0
E2E0
E2F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E3A0  
E3B0
E3C0
E3D0
E3E0
E3F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E4A0  
E4B0
E4C0 涿
E4D0
E4E0
E4F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E5A0  
E5B0
E5C0
E5D0
E5E0
E5F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E6A0  
E6B0
E6C0
E6D0
E6E0 驿
E6F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E7A0  
E7B0
E7C0
E7D0
E7E0
E7F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E8A0  
E8B0
E8C0
E8D0
E8E0
E8F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
E9A0  
E9B0
E9C0 槿
E9D0
E9E0
E9F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
EAA0  
EAB0
EAC0
EAD0
EAE0
EAF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
EBA0  
EBB0
EBC0
EBD0
EBE0
EBF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
ECA0  
ECB0
ECC0
ECD0
ECE0
ECF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
EDA0  
EDB0
EDC0
EDD0
EDE0
EDF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
EEA0  
EEB0
EEC0
EED0
EEE0
EEF0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
EFA0  
EFB0
EFC0
EFD0
EFE0
EFF0  

 

GB 2312 Character Map, 0xF0A0 - 0xF7FF

  0 1 2 3 4 5 6 7 8 9 a b c d e f
F0A0  
F0B0
F0C0
F0D0
F0E0
F0F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
F1A0  
F1B0
F1C0
F1D0
F1E0
F1F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
F2A0  
F2B0
F2C0
F2D0
F2E0
F2F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
F3A0  
F3B0
F3C0
F3D0
F3E0
F3F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
F4A0  
F4B0
F4C0
F4D0
F4E0 羿
F4F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
F5A0  
F5B0
F5C0 趿
F5D0
F5E0
F5F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
F6A0  
F6B0
F6C0 鱿
F6D0
F6E0
F6F0  
  0 1 2 3 4 5 6 7 8 9 a b c d e f
F7A0  
F7B0
F7C0
F7D0
F7E0
F7F0  

 

 

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

// 테이블 생성 및 데이터 삽입

#include <afxdb.h>
#include <atlstr.h>
#include <stdio.h>

int main()
{
 CDatabase  m_db;

 CString  sDriver = L"MICROSOFT EXCEL DRIVER (*.XLS)";
 CString  sExcelFile = L"test.XLS";
 CString  sSql;

 if(m_db.IsOpen() == TRUE)
 {
  m_db.Close();
 }
 
 //테이블 생성
 sSql.Format(L"DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=\"%s\";DBQ=%s",
  sDriver, sExcelFile, sExcelFile);
 if(m_db.OpenEx(sSql, CDatabase::noOdbcDialog))
 {
  sSql.Format(L"CREATE TABLE %s (%s, %s, %s)"
   ,_T("TEST3")
   ,_T("Seq NUMBER")
   ,_T("Name TEXT")
   ,_T("Phone TEXT")  
   );
  m_db.ExecuteSQL(sSql); 
 }

 //레코드 삽입
 sSql.Format(L"INSERT INTO %s(Seq, Name, Phone) VALUES(%d, '%s', '%s')"
  ,_T("TEST3")
  ,1
  ,_T("홍길동")
  ,_T("123-4567")
  );

 m_db.ExecuteSQL(sSql);

 if(m_db.IsOpen() == TRUE)
 {
  m_db.Close();
 }
}


// 테이블 읽어오기

#include <afxdb.h>
#include <atlstr.h>
#include <stdio.h>

int main()
{

 CDatabase  m_db;

 CString  sDriver = L"MICROSOFT EXCEL DRIVER (*.XLS)";
 CString  sExcelFile = L"test.XLS";
 CString  sSql;


 if(m_db.IsOpen() == TRUE)
 {
  m_db.Close();
 }

 sSql.Format(L"DRIVER={%s};DSN='';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=\"%s\";DBQ=%s",
  sDriver, sExcelFile, sExcelFile);
 if(m_db.OpenEx(sSql, CDatabase::noOdbcDialog))
 {
  sSql.Format(L"SELECT * FROM %s", L"TEST3");
  CRecordset rs(&m_db);
  rs.Open(CRecordset::forwardOnly, sSql, CRecordset::readOnly);

  CString  sFields[20];
  int   iFieldCount  = 0;


  iFieldCount = rs.GetODBCFieldCount();
  wprintf(L"%d\n", iFieldCount);
  while(!rs.IsEOF())
  {
   for(int i=0; i< iFieldCount; i++)
   {
    //한글을 못읽어오는 문제점이 있음   
    rs.GetFieldValue((short) i, sFields[i]);
    wprintf(L"%s\n", sFields[i]);
   }

   rs.MoveNext();
  }
 }

 if(m_db.IsOpen() == TRUE)
 {
  m_db.Close();
 }
}


 

 

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

Console Function - MSDN  (0) 2011.08.13
Chinese Character Codes  (0) 2011.08.13
cast 연산자  (0) 2011.08.13
c++ 추상 기초클래스  (0) 2011.08.13
Byte Order  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

cast 연산자

Development/C/C++ 2011. 8. 13. 15:04

1. static_cast

 

C++ 언어에서 캐스트(cast) 연산은 가장 보편적으로 사용되는 연산 입니다. 그러나 이것 만큼 정확한 의미를 모르고 사용되는 연산 또한 없을 것입니다. Visual C++ 가 제공하는 캐스트는 크게 묵시적 캐스트(implicit cast)와 명시적 캐스트(explicit cast) 두 가지로 나눌 수 있습니다. 묵시적 캐스트는 문법적인 키워드가 존재하지 않습니다. 이에 반해 명시적 캐스트는 문법적인 키워드를 가지고 있는데, 여기에는 (), static_cast, const_cast, reinterpret_cast , dynamic_cast 다섯 가지가 있습니다. 이것에 대해 하나 하나 살펴 보겠습니다.

static_cast

static_cast 는 묵시적 캐스트(implicit cast)와 일차적으로 같습니다. 타입 변환이 필요한 상황에서 특별히 캐스트 연산자를 사용하지 않을 때, 컴파일러는 묵시적 캐스트를 수행 합니다.

다음 코드를 보겠습니다.

int i = 65;

char c = i;

위의 코드는 정수 i 변수의 값을 문자 타입 변수 c 에 대입 합니다. 이때 컴파일러는 묵시적 캐스트(implicit cast)를 수행합니다.

컴파일러의 의한 묵시적 캐스트(implicit cast)는 ‘허용’ 과 ‘컴파일러에 의한 값 변환’ 두 가지로 볼 수 있습니다. ‘허용’ 이라 하는 것은 컴파일러가 오류를 발생 시키는 않고 컴파일 한다는 뜻입니다.

그리고 ‘컴파일러에 의한 값 변환’ 은 값을 변환하기 위하여 컴파일러가 만들어 내는 기계어 코드를 의미 합니다. 그런데 컴파일러가 값을 변환하기 위하여 만들어 내는 기계어 코드는 당연히 컴파일 시점일 될 것입니다.

앞에서 static_cast 가 묵시적 캐스트(implicit cast)와 일차적으로 같다고 했습니다. 즉 static_cast 도 묵시적 캐스트와 같이 ‘허용’ 과 ‘컴파일러에 의한 값 변환’ 이라는 두 가지 관점이 있고, ‘컴파일러에 의한 값 변환’ 을 위하여 컴파일러가 기계어 코드를 만들어 내는 시점이 컴파일 시점 입니다.

그래서 이것을 정적 캐스트(static cast) 라고 부릅니다.

 

그럼 왜 굳이 묵시적 캐스트(implicit cast)도 있는데 정적 캐스트(static cast)를 사용할 까요?

문법적 엄격함 입니다. 프로그래머는 스스로 문법적으로 엄격하게 작성하는 것이 버그 없는 프로그램을 만들기 위해 좋습니다. 제가 야구를 좋아하는데, 묘기를 잘 부리는 야구 선수는 결코 야구를 잘하는 선수가 아닙니다. 모든 것을 쉽고, 그리고 엄격하게 플레이 하는 선수가 진짜 야구 잘하는 선수입니다.

프로그래밍도 이와 같습니다.

그래서 c = i; 보다는 c = static_cast<char>(i); 가 더 바람직합니다.

이제 묵시적 캐스트(implicit cast)와 정적 캐스트(static cast)의 ‘허용’ 관점에 대해 이야기 해 보겠습니다. ‘컴파일러에 의한 값 변환’ 을 위해 컴파일러가 기계어 코드를 생성하기전에 컴파일러는 캐스트 상황이 안전한 지에 대해 판단하고, 안전 하다면 이를 허용하게 됩니다.

 

어떤 변수가 값을 가지고 있다면 그 변수에 어떤 값이 들어 있던 그것은 항상 안전 합니다. 그러나 만약 어떤 변수가 포인터를 가지고, 그 포인터가 가리키는 메모리가 유효하지 않다면 그것은 안전하지 않습니다.

그렇기 때문에 컴파일러가 캐스트를 허용하기 위하여 안전성을 검사하는 기준은 포인터 변수에 한해서 수행합니다. 아래의 코드를 보겠습니다.

 

char c = 'A';

char* pc = &c;

int* pi = pc;

 

위의 코드에서 int* pi = pc; 는 컴파일러에 의해 허용되지 않은 문장으로 오류를 발생시킵니다. pi 가 가리키는 메모리는 실제 문자 변수이고, 만약 pi 를 이용한다면 4 byte 액세스(정수 타입이므로)가 이루어져 다운될 가능성이 있습니다.

이것을 사전에 막고자 컴파일러는 이 문장을 허용하지 않습니다. 결론적으로 묵시적 캐스트(implicit cast)와 정적 캐스트(static cast)는 포인터 타입 캐스트에 대해 동일 타입인 경우에 한하여 허용합니다.

앞에서 이야기 한 것과 같이 대부분의 경우 정적 캐스트(static cast) 와 묵시적 캐스트(implicit cast)는 대부분 동일합니다. 다른 점은 없을까요?

class CBase { public: int a; };

class CDerived : public CBase { public: int b; };

 

위와 같이 클래스가 선언 되어 있을 때, 다음 코드를 보겠습니다.

 

CDerived* pDerived = new CDerived; CBase* pBase;
pBase = pDerived;

 

위의 pBase = pDerived; 이 허용 될까요? 포인터 타입의 캐스트 이고, 두 타입(pBase 와 pDerived)이 다른 타입이므로 허용되지 않아야 하겠지만 허용됩니다.

클래스 포인터에 대해 ‘is a’ 관계가 성립 한다면 묵시적 캐스트(implicit cast)는 허용됩니다. 왜냐하면 ‘is a’ 가 성립하는 두 타입 이라면 논리적으로도 하위 클래스가 상위 타입 데이터를 최소한 포함하기 때문에 100 % 안전하기 때문입니다.

 

물론 정적 캐스트(static cast)도 똑 같습니다.

즉 pBase = static_cast<CBase*>(pDerived); 도 허용됩니다.

계속 묵시적 캐스트(implicit cast) 와 정적 캐스트(static cast)의 같은 점만을 이야기 하고 있죠?

 하나 다른 것이 있습니다.

위의 클래스 선언을 그대로 포함한 상태에서 다음 코드를 보겠습니다.

 

CBase* pBase = new CDerived; CDerived* pDerived;

pDerived = pBase;

 

위의 pDerived = pBase; 이 허용 될까요? 허용 되지 않습니다.

왜냐하면 pDerived 가 실제 가리키고 있는 것은 CBase 객체인데, 타입이 CDerived 으로 되었으므로 이를 이용하여 CDerived 전용 멤버인 b 를 액세스 함으로서 다운될 가능성을 가지고 있기 때문입니다. 그러나 이 경우 정적 캐스트(static cast)는 가능 합니다.

즉 pDerived = static_cast<CDerived*>(pBase); 는 가능 합니다.

이것 하나가 묵시적 캐스트(implicit cast) 와 정적 캐스트(static cast)의 차이점 입니다. 즉 클래스 포인터에 대해 묵시적 캐스트(implicit cast)는 ‘is a’ 관계가 성립 하는 경우만 허용하고, 정적 캐스트(static cast)는 ‘is a’ 뿐만 아니라 상속 관계만 성립한다면 항상 허용합니다.

물론 상속 관계조차 성립하지 않는다면 정적 캐스트(static cast)도 캐스트를 허용하지 않습니다. 다음에는 reinterpret_cast 와 const_cast 에 대해 살펴 보겠습니다. 오타 있으면 지적해 주시면 감사하겠습니다.

 

 

2. reinterpret_cast

 

reinterpret_cast 는 연관성이 없는 포인터 타입을 변환하기 위해서 사용 됩니다.

이것은 static_cast 와 비교해 생각해 보는 것이 이해하기 쉽습니다. 앞에서 static_cast 와 묵시적 캐스트(implicit cast)는 연관성 있는 데이터 타입에 대해서 캐스트가 가능하다고 했습니다.

즉 연관성이 없는 데이터에 대해서는 static_cast 와 묵시적 캐스트(implicit cast)를 사용할 수 없습니다.

char c = 'A';

char* pc = &c;

int* pi = pc; // int* pi = static_cast<int*>(pc); (X)

 

위 코드의 경우 int* pi = pc; 은 문법적으로 허용되지 않습니다. pc 와 pi 는 연관성이 없기 때문입니다.

그러나 이 경우 int* pi = reinterpret_cast<int*>(pc); 를 사용 한다면 캐스트(cast)가 될 것입니다. 앞의 예 외에 reinterpret_cast 는 클래스 타입에 대해 클래스가 연관 관계가 없는 경우에도 캐스트(cast)를 할 수 있습니다.

물론 이것이 캐스트(cast) 된다고 바람직 한 것이 아닙니다. 논리적으로 안전하지 않을 수 있기 때문입니다.

int* pi = reinterpret_cast<int*>(pc); 가 문법적으로 허용 된다고 하지만 pi 를 이용하여 메모리에 접근 한다면 프로그램이 다운될 수도 있기 때문입니다.

reinterpret_cast 는 C/C++ 개발자가 일반적으로 사용 했던 () 연산자를 이용한 캐스트와 대부분 같습니다. () 는 모든 경우에 강제 캐스팅을 수행 합니다.

물론 논리적으로 안전하지는 않는 경우가 있을 수 있지만,,,

 

reinterpret_cast 도 강제 캐스팅을 수행합니다.

reinterpret_cast 와 () 의 차이는 reinterpret_cast 는 포인터 타입에 대한 캐스트만 수행할 수 있다는 것과 const 타입과 volite 타입의 포인터 타입에 대해서는 캐스트를 수행할 수 없다는 것입니다.

 

int i = 65;

char c = reinterpret_cast <char>(i);

 

물론 위와 같이 코딩하실 분은 없으시겠지만 위의 코드에서 reinterpret_cast 는 허용되지 않습니다. 저의 프로그래밍 철학으로 볼 때, 저는 이 reinterpret_cast 의 사용을 권장 하지 않습니다.

저는 항상 문법적 허용과 논리적 허용이 일치해야 한다고 생각합니다. 그러나 reinterpret_cast 는 문법적 허용과 논리적 허용이 일치 하지 않는 경우도 있습니다.

물론 () 연산자를 이용한 캐스트도 마찬가지 입니다. 다음에는 dynamic_cast 와 const_cast 에 대해 살펴 보겠습니다. 오타 있으면 지적해 주시면 감사하겠습니다.

 

 

3. const_cast

 

 const_cast 는 포인터 타입과 참조형에 대해서만 사용됩니다. 그리고 동일 타입의 포인터와 참조형에 대해서만 사용됩니다.

동일 타입 외에 어떤 연관 관계 타입(is a 타입 포함)에 대해서도 허용되지 않습니다. 그렇다면 const_cast 의 사용 목적은 무엇일까요?

 

const CSample* pSample = new CSample;

CSample* pSample1 = pSample; pSample1->m = 10;

 

위의 코드에서 처럼 상수 목적으로 생성된 CSample 객체가 필요에 의해 변수 목적(pSample1->m = 10)으로 사용되어야 한다면 이것을 일단 변수 객체 포인터로 변환해야 합니다.

그러나 이 상황(CSample* pSample1 = pSample)에서 묵시적 캐스트(implicit cast)나 static_cast 또는 reinterpret_cast 를 사용한다면 컴파일러는 이를 허용하지 않을 것입니다.

이 때 사용할 수 있는 것이 const_cast 입니다.

즉 위의 코드의 경우 다음과 같이 사용해야 합니다.

 

CSample* pSample1 = const_cast<CSample*>(pSample);

 

const_cast 는 보시는 분에 따라 명시적 캐스트 연산자인 () 도 있는데, 왜 const_cast 가 필요한지 의문을 갖는 분도 있을 것입니다. 이것 역시 문법적인 엄격함 때문에 사용됩니다.

저는 사실 const_cast 사용을 권장하지 않습니다. 모든 변수는 선언 시에 목적을 가지고 선언 되어야 하는데, 만약 변수 목적으로 사용될 가능성이 있었다면 애초에 상수로 선언되어서는 안됩니다. 프로그램 버그는 목적을 가지지않고 선언된 변수나 목적에 어긋나게 사용되는 변수에 의해 발생되는 경우가 많습니다.

 

C++ 언어에서 많이 사용되지 않는 한정자 중에 volatile 타입 한정자가 있습니다. const_cast 는 volatile 타입 객체를 일반 타입 포인터나 참조형으로 변환하기 위해서도 사용됩니다.

 

volatile BOOL g_bCalc = FALSE; :

BOOL* p = const_cast<BOOL*>(&g_bCalc);

 

물론 이 경우도 저는 권장하고 싶지 않습니다.

 

 

4. dynamic_cast

 

dynamic_cast 는 상당히 능동적인 캐스트 입니다. 다른 캐스트와 달리 dynamic_cast 는 실행 중에 캐스트가 이루어 집니다. 실행 중에 캐스트의 대상이 되는 데이터를 능동적으로 판단하여 실행 코드가 캐스트를 수행합니다.

dynamic_cast 의 문법적 형식은 다음과 같습니다.

 

dynamic_cast <타입>(표현식)

 

표현식은 가상함수와 RTTI(Runtime Type Information)를 포함하는 클래스에 대한 포인터나 참조형, 혹은 객체 일 수 있습니다. 그리고 타입은 역시 가상함수와 RTTI 를 포함하는 클래스의 포인터나 참조형일 수 있습니다.

타입에 한가지 예외적으로 void* 를 명시할 수도 있습니다. 표현식과 타입의 형식만 맞다면 컴파일러는 컴파일을 허용합니다. 표현식과 타입이 연관 관계가 있던 없던 가상함수와 RTTI 를 포함하기만 하면 dynamic_cast 는 무조건 컴파일을 허용합니다. 다른 캐스트와 달리 dynamic_cast 는 컴파일러의 허용 관점 보다는 실행 시 허용관점이 더 중요합니다.

우리도 실행 시 허용에 관심을 두어야 합니다.

RTTI(Runtime Type Information)란 클래스 타입 정보를 가지고 있는 클래스(type_info) 입니다. RTTI 는 컴파일러가 내부적으로 사용하는 정보 클래스로 아래와 같이 정의 되어있습니다.

 

class type_info {

    public: virtual ~type_info();

    int operator==(const type_info& rhs) const;

    int operator!=(const type_info& rhs) const;

    int before(const type_info& rhs) const;

    const char* name() const;

    const char* raw_name() const;

    private: void *_m_data;

    char _m_d_name[1];

    type_info(const type_info& rhs);

    type_info& operator=(const type_info& rhs);

};


type_info 클래스의 _m_data 멤버 변수는 이 글을 쓰는 저도 이해하지 못하며 NULL 로 설정되어 있습니다. 다음 _m_d_name 멤버 변수는 실제 char 하나로 구성되어 있지만 클래스 이름을 가지고 있는 배열로서 type_info 가 컴파일러에 의해 만들어 질 때 클래스 이름을 문자열로 갖도록 확장되어 생성 됩니다.

RTTI(Runtime Type Information) 를 만들기 위해 우리가 해야 할 일은 /GR 컴파일 옵션을 지정 하기만 하면 됩니다.

 

 Visual C++ 6.0 같은 경우 [Project]-[Setting] 메뉴를 선택하여 [Project Settings] 대화상자를 나타낸 후, [C/C++] 탭의 [C++ Language] 카테고리 컴파일 옵션을 선택하고, [Enable Run-Time Type Information (RTTI)] 를 선택하면 됩니다. 앞의 옵션이 선택된 경우 컴파일러는 RTTI 를 컴파일 하는 모든 클래스에 대해 생성합니다.

그러나 실제 각 클래스가 RTTI 를 사용하기 위해서는 각 클래스는 최소한 하나 이상의 가상함수를 가져야 합니다. 만약 클래스가 가상함수를 하나도 가지지 않는다면 해당 클래스는 타입으로는 RTTI 를 이용할 수 있지만 객체로는 RTTI 를 이용할 수 없습니다.

우리가 지금 알아보고 있는 dynamic_cast 도 객체의 RTTI 를 이용한 동적(실행시) 캐스트 이므로 dynamic_cast 를 위해서도 해당 클래스는 최소한 하나 이상의 가상함수를 가져야 합니다. 컴파일러는 RTTI 와 객체를 연결하기 위해서 가상함수 포인터 테이블을 이용합니다. 이것은 변칙입니다.

언제부터 RTTI 가 C++ 언어에 도입 되었는지 알 수 없지만, 원래 C++ 언어의 가상함수 포인터 테이블은 순수한 가상함수에 대한 함수 포인터 배열입니다. RTTI 와 객체의 연결을 위해 C++ 언어는 가상함수 포인터 테이블 앞에 4 byte 를 만들고 이것을 RTTI 와의 연결 고리로 사용하고 있습니다.

class CBase {

public: int m_base;

virtual void fun1 (void);

virtual void fun2 (void);

};

 

void CBase::fun1 (void) {

cout << "Base fun1" << endl;

}

 

void CBase::fun2 (void) {

cout << "Base fun2" << endl;

}

 

class CDerived : public CBase {

 public: int m_derived; virtual void fun3 (void);

};

 

int main(int argc, char* argv[]) {

CBase* p1 = new CBase;

CDerived* p2 = new CDerived;

CDerived* p3 = dynamic_cast<CDerived*>(p1);

CBase* p4 = dynamic_cast<CBase*>(p2);

return 0;

}

 

RTTI 가 생성된 경우 객체와 가상함수 포인터 테이블 모양이 조금 복잡해 집니다.

객체(p1, p2)는 여전히 가상함수 포인터 테이블을 가리키고 있지만 가상함수 포인터 테이블 앞에 숨겨진 4 byte 가 컴파일러에 의해 추가로 만들어 집니다.

그리고 바로 이 숨겨진 4 byte 가 클래스의 RTTI 포인터 테이블과 객체의 연결 고리로 사용됩니다.

 

RTTI 포인터 테이블은 객체 자신의 RTTI 나 상위 클래스의 RTTI 를 가리키는 또 다른 연결 고리입니다. 여러 단계에 걸쳐 상속된 경우 실제 모양은 위의 그림보다 복잡해 집니다. 위의 그림은 원리를 설명하기 위하여 간단히 그려본 것입니다.

프로그램이 dynamic_cast 를 이용하여 캐스트를 한 경우 실행 코드는 dynamic_cast 의 표현식에 기술된 객체를 이용하여 RTTI 포인터 테이블을 검색하고, 만약 RTTI 포인터 테이블 상에 일치하는 RTTI 가 존재 한다면 표현식에 기술된 객체의 타입을 변환하여 반환하고, RTTI 포인터 테이블 상에 일치하는 RTTI 가 존재 하지 않는다면 dynamic_cast 는 NULL(0) 을 반환할 것 입니다.

앞의 코드의 경우 p3 는 NULL 을 반환하고 p4 는 유효한 값을 반환할 것입니다.

이와 같이 dynamic_cast 는 다른 캐스트와 달리 표현식의 실제 객체를 추적하여 캐스트를 수행하므로 상당히 능동적이 캐스트라 할 수 있습니다. 잘 설계된 클래스를 바탕으로 어플리케이션이 개발 될 때 어플리케이션 상의 클래스 포인터 타입은 대부분 상위의 몇 가지 타입으로 운영됩니다. 그러나 간혹 실제 객체의 클래스 타입에 따라 어떤 논리를 처리해야 하는 경우가 생기는데, 이때 우리는 dynamic_cast 를 다음과 같이 이용할 수 있습니다.

 

if (dynamic_cast<CDerived*>(p)) { : : }

 

위의 if 블록은 p 가 CDerived 타입 인 경우만 수행될 것입니다. dynamic_cast 만큼 일반적이지는 않지만 RTTI 가 사용되는 경우가 한가지 더 있습니다.

바로 typeid 연산자가 바로 그것입니다. typeid(표현식) typeid 는 표현식이 RTTI 를 가지고 있다면 그것의 RTTI 클래스(type_info) 참조형을 반환합니다. RTTI 클래스(type_info) 참조형을 이용하여 클래스 이름이나 비교를 수행할 수 있습니다.

 typeid 를 사용할 때 주의할 것은 클래스 포인터가 전달된 경우 클래스 포인터 타입에 대해 별도의 RTTI 클래스 참조형이 반환된다는 것이다.

 

int main(int argc, char* argv[]) {

CBase* p1 = new CBase;

CDerived* p2 = new CDerived;

const type_info& t = typeid (p1);

const type_info& t1= typeid(*p1);

return 0;

}

 

위의 typeid(p1) 과 typeid(*p1) 은 서로 다른 type_info 가 반환됩니다. typeid(*p1) 은 dynamic_cast 와 같이 p1 객체를 추적하여 RTTI 를 알아내고 그것의 type_info 참조형으로 반환합니다. 그러나 typeid(p1) 은 p1 포인터 타입(CBase*)에 대해 RTTI 가 반환됩니다.

이때 반환되는 RTTI 는 객체와 어떠한 연결 고리도 가지지 않는 RTTI 입니다.

 

 

 

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

Chinese Character Codes  (0) 2011.08.13
CDatabase 을 사용한 엑셀 데이터 수정  (0) 2011.08.13
c++ 추상 기초클래스  (0) 2011.08.13
Byte Order  (0) 2011.08.13
ASCII Code Table  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,

c++에서 아주 중요한 class에 대해서 공부하고 있군요..
질문자는 class가 내부적으로 어떻게 돌아가는지 잘 모르시는 것 같습니다..
사실 c++이라는게 워낙에 프로그래머가 모르는 일을 많이 해서 직관적으로 다가오지 않는게
사실입니다..
저 역시 class를 공부했을때 질문자처럼 답답함을 느꼈죠..
이 내용은 저에게 많은 가르침을 주셨던 분에게 배웠던 내용입니다..
어설프게 설명하여 그 분의 이름에 먹칠이나 하지 않을까 걱정이지만 그래도 설명을 해보겠습니다..^^
(이해를 못했다면 제가 부족한탓임..^^;;)

 

흔희들 c++를 객체지향 c언어를 절차적 언어라고 하는데요..
아이러니하게도 c++의 클래스는 c언어로 표현할 때 직관적으로 이해할 수 있습니다..
(질문자가 c언어를 모를수도 있기때문에 쉽게 구조체로 표현한다고 생각하세요..
 또한 printf를 모를수도 있기때문에 cout으로 표현하겠습니다..)

 

1.생성자와 소멸자..
아래는 클래스로 표현한 간단한 생성자와 소멸자 예제입니다..

 

class CTest
{
public:
 CTest()
 {
  cout << "생성자" << endl;
 }
 ~CTest()
 {
  cout << "소멸자" << endl;
 }
 
 int i;
};

void main()
{
 CTest *test;

 test = new CTest;

 delete test;
}

 

출력내용은 "생성자" "소멸자" 입니다..
위의 예제로 보면 new를 할때 생성자가 호출되고 delete를 할 때 소멸자가 호출된다는것은 알겠지만
직관적이지 못합니다..
저 내용을 c언어로 표현하면 아래와 같습니다..

 

//*
typedef struct CTest
{
 int i;
}CTest; //*/

 

void CTest_Init(CTest* test)
{
 cout << "생성자" << endl;
}
void CTest_Clear(CTest* test)
{
 cout << "소멸자" << endl;
}

 

void main()
{
 CTest *test;

 test = (CTest*)malloc(sizeof(CTest));
 CTest_Init(test);

 CTest_Clear(test);
 free(test);

 getch();
}

 

보시면 알겠지만 c언어로 표현을 하니까 생성자와 소멸자가 무엇인지 직관적으로 보입니다..
c++에서 new를 한다는것은 malloc 후에 생성자 함수를 호출하는것이고 delete를 한다는것은
소멸자함수를 호출한후에 free를 한다는것을 알 수 있습니다..
new,delete와 c언어와의 관계를 보기쉽게 아래와같이 표현했습니다..

new  ==  malloc() -> Init();호출
delete == Clear();호출 -> free()

즉 c++에서의 객체생성은 저와같은 과정을 생략하고 있었던 것입니다..


2. 상속
아래는 클래스를 이용한 간단한 상속 예제입니다..

 

class A
{
public:
 A()
 {
  cout << "어미 생성됐다.." << endl;
 }
 ~A()
 {
  cout << "어미 소멸됐다.." << endl;
 }
 
 int i;
};

 

 

class B : public A
{
public:
 B()
 {
  cout << "자식 생성됐다.." << endl;
 }
 ~B()
 {
  cout << "자식 소멸됐다.." << endl;
 }

 char j;
};


void main()
{
 B *b;

 b = new B;
 delete b;

 getch();
}


실행을 해보면 생성자와 소멸자의 호출순서가 흥미롭습니다..
어미->자식->자식->어미 이런식으로 생성자와 소멸자가 호출됨을 알 수 있습니다..
하지만 이러한 내용은 new와 delete에서 내부적으로 지가 알아서 해버리기때문에
프로그래머한테 직관적으로 다가오지 않습니다..
c언어로 표현을 해보죠..


typedef struct A
{
 int i;
}A;

 

void A_Init(A* a)
{
 cout << "어미 생성" << endl;
}
void A_Clear(A* a)
{
 cout << "어미 소멸" << endl;
}

 

 

typedef struct B
{
 A parent;        // 이게 상속의 정체다..
 char j;
}B;

 

void B_Init(B* b)
{
 cout << "자식 생성" << endl;
}
void B_Clear(B* b)
{
 cout << "자식 소멸" << endl;
}

 

void main()
{
 B *b;

 b = (B*)malloc(sizeof(B));
 
A_Init(&b->parent);
 B_Init(b);


 B_Clear(b);
 A_Clear(&b->parent);
 free(b);

 getch();
}


B구조체에서 상속을 어떻게 표현했는지 잘 보세요..
바로 A객체를 포함함으로써 상속이 표현됐음을 알 수 있습니다.. 이게 바로 c++에서 상속의 정체죠..
또한 B객체를 생성했을때 생성자와 소멸자의 호출 역시 c언어에서 더욱더 직관적으로 알 수
있게 되었습니다..
상속된 객체를 new나 delete를 했을경우 c++내부에서는 아래와같이 진행이 된다는 것이죠..

 

new == malloc() -> 어미생성자함수호출 -> 자식생성자함수호출
delete ==  자식소멸자함수호출 -> 어미소멸자함수호출 -> free()

 

자 여기에서 질문자의 마지막 질문의 의문이 풀렸습니다..

 

class A
class B :public A 일때

A *a = new B;

 

당연히 되겠죠?? B구조체에서 봤던것처럼 B는 내부적으로 낮은주소에 A객체를 포함하고 있기때문에
A포인터형으로 B를 가리키게 된다고 해도 유효한 영역이므로 가능한것입니다..

 

A포인터 -->  [A]|j
B포인터 -->  [A]|j

 

둘다 상관없다는 말입니다.. 그런데 아래와같이 B구조체를 만들었다고해봅시다..

 

tyepdef struct B
{
   char j;
   A parent;
}

 

이건 상속이 아니고 위임입니다.. 상속은 항상 상속의 대상이되는 객체가 구조체의 맨위에 위치해야합니다.

 

A포인터 -->  j|[A]
B포인터 -->  j|[A]

 

위와같이 A포인터가 j영역을 가리키고있기때문에 성립이 안되는거죠..
이제 확실히 상속이 무엇인지 알겠죠??


3. 다중상속의 사기성..
A,B,C클래스가 있고 C클래스가 A,B를 상속받았다고 합시다..
그런데 이것을 구조체로 표현하면 아래와 같을것입니다..

 

tyepdef struct C
{
   A parent1;
   B parent2;
   .
   .
   .
}C;

 

메모리 배치는 아래와같이 되겠죠..


[A][B]...

 

그런데 이상하죠..?? 분명 C는 A,B를 둘다 상속했으므로 A,B포인터로 다 표현가능해야합니다..
그런데 c언어에서 보니까 아래와같은 문제가 생기게됩니다..

 

A포인터 --> [A][B]...
B포인터 --> [A][B]...

 

A포인터로 C를 가리키면 문제가 안되지만 B포인터로 C를 가리키면 문제가됩니다..
왜냐하면 B포인터는 B영역이 아닌 A영역을 가리킬테니까여..
그래서 c++에서는 프로그래머가 예상치못한 과부하가 걸리는 어떤 조작을 내부적으로 해버립니다..
아래와같이 말이죠..

 

A포인터 --> [A][B];
B포인터 --> c++내부조작 --> [B][A];

 

c++을 처음공부하는 프로그래머라면은 이런현상을 알 수 없습니다..
왜냐하면 내부적으로 프로그래머 모르게 처리해버리니까요..
즉 어설프게 c++를 했다가는 끊임없이 프로그래머를 속이는 컴파일러에 당할수 밖에 없다는 거죠..

다른예를 들어보도록하죠.. 다이아몬드형태의 상속도 문제가 됩니다..
아래와같은 상속이 있다고 해보죠..

 

A <--  B   <--
  <--  C   <-- D

 

위의 상속계통도는 B와 C는 A를 상속받았고 D는 다시 B,C 모두에게 다중상속을 받았습니다..
그러면 이것을 c언어로 표현한다면 아래와같이 되겠죠..

 

struct B
{
   A parent;
   int b
};

struct C
{
   A parent;
   int c
};

struct D
{
   B parent1;
   C parent2;
   int d
};

 

왜 이것이 문제가 될까요?? 이것을 메모리배치로 표현을 하면 아래와 같습니다..

 

D포인터  -->  [[A][b]] [[A][c]] [d]

 

앞에A는 B의 A이고 뒤에 A는 C의 A입니다.. 그런데 만약 상속받은 A객체의 멤버에 접근한다면
어느 A멤버에 접근해야 하죠?? 앞에 A?? 뒤에 A??
이건 큰 문제죠.. 그래서 저런 다이아몬드 상속의 문제점을 해결하기 위해 c++에서는 상속에 virtual 키워드를
사용하게 만들어놓았습니다.. 아래와같이 말이죠..

 

class B : virtual public A
{
};

class C : virtual public A
{
};

class D : public B, public C
{
};

 

아무리 virtual키워드를 사용하여 상속을 한다고 하지만 c++이 내부적으로 과부하를 일으킨다는것은
너무도 뻔합니다..
결과적으로 다중상속은 "별로좋지않다" 라는것입니다.. 다중상속을 많이 할 수록 문제점은 기하급수적으로 늘어만 가죠..
때문에 자바와 c#에서는 문제가 많은 다중상속을 지원하지 않습니다..
대신 인터페이스라는 개념을 도입하여 다중상속의 문제점을 해결하고 있죠..(인터페이스는 c++에서 안나오므로 설명생략..)
하지만 c++에서는 그러한 개념이 없기때문에 조심해서 다중상속을 사용하면 됩니다.. ㅡㅡ;

 

4. 가상함수..
이제 질문자가 궁금해하고 있는 가상함수부분에 대해서 설명하도록 하죠..
아래는 간단한 가상함수의 예입니다..

class A
{
public:
 A()
 {
  i = 100;
 }
 ~A()
 {
 }

 virtual void Print()
 {
  cout << i << endl;
 }

 int i;
};

 

 

class B : public A
{
public:
 B()
 {
  ch = 'a';
 }
 ~B()
 {
 }

 virtual void Print()
 {
  cout << ch << endl;
 }

 char ch;
};

 

 

void main()
{
 A *a = new A;
 A *b = new B;

 a->Print();
 b->Print();

 delete a;
 delete b;

 getch();
}

 

실행해보면 알겠지만 a->Print()와 b->Print()는 서로 다른 메소드입니다..
도대체 virtual이 뭐길래 실행도중에 저렇게 Print가 바뀌는것일까요???
c언어로 가상함수를 구현해보면 쉽게 알 수 있습니다..
c언어에서는 함수포인터를 사용하여 가상함수를 구현합니다..
위의 예제를 c언어로 구현해보면 아래와같습니다..


struct A;
typedef void (*Print)(A* a);        // 함수포인터..

 

 

 

typedef struct A
{
 Print print;
 int i;
}A;

 

void A_Init(A* a)
{
 a->i = 100;
}
void A_Clear(A* a)
{
}
void A_Print(A* a)
{
 cout << a->i << endl;
}

 

 


typedef struct B
{
 A parent;
 char ch;
}B;

 

void B_Init(B* a)
{
 a->ch = 'a';
}
void B_Clear(B* a)
{
}
void B_Print(A* a)
{
 cout << ((B*)a)->ch << endl;
}

 

 

void main()
{
 A *a,*b;

 

 a = (A*) malloc (sizeof(A));
 a->print = A_Print;           // 이것이 가상함수의 정체다..
 A_Init(a);

 

 b = (A*) malloc (sizeof(B));
 b->print = B_Print;           // 가상함수세팅..
 A_Init(a);                    // 상속받았으므로 부모부터 생성자호출..
 B_Init((B*)b);               
// 자식생성자 호출..

 

 a->print(a);
 b->print((A*)b);

 

 B_Clear((B*)b);
 A_Clear(a);
 free(b);

 

 A_Clear(a);
 free(a);

 getch();
}

결과는 클래스로 구현했을때와 같습니다.. 즉 각각의 함수는 메모리가 할당되는 순간
배치되어 실시간적으로 Print메소드가 서로 다르게 구현되었던 것이었습니다..
c++에서는 가상함수테이블이 라는것이 있어서 A_Print,B_Print의 함수포인터를 테이블에 저장해놓습니다..
그리고 클래스의 메모리가 생성될때 함수포인터를 테이블에서 읽어와 위에처럼 세팅하죠..
new 를 사용하다보면 뒤에 클래스명 적어주죠?? 바로 그 클래스명을 해쉬코드값으로 하여 해당 테이블을 찾아
함수포인터를 세팅하는것입니다..
아래와 같이 말이죠..

 

new == malloc실행 -> 구조체명을 해쉬코드로 하여 함수포인터를 얻어와세팅  -> 부모생성자함수호출 -> 자식생성자함수호출
delete == 자식소멸자함수호출 -> 부모소멸자함수호출 -> free실행

 

c++에서는 위의 과정이 new명령어의 내부적으로 실행되기때문에 프로그래머는 알 수 없습니다..
그러고보면 new나 delete가 상당히 많은 일을 합니다..^^

 

5. 순수가상함수..
위의 예제에서 A클래스가 아래와 같이 추상클래스라고 해봅시다..


class A
{
public:
 A()
 {
 }
 ~A()
 {
 }

 virtual void Print() = 0;        // 순수가상함수..
};

 

이때 A를 new로 메모리생성하게 되면 함수포인터를 얻어와 세팅되는 부분이 생략됩니다..
c언어 입장에서 보면 malloc 후에 a->print = NULL; 로 세팅한다는 얘기죠..
때문에 a->print(a); 를 실행할 수가 없습니다.. (NULL은 실행못하니까여..)

 

typedef struct A
{
 Print print;
}A;

 

위와같이 Print 의 포인터함수 선언만 있을뿐 실제로 print에 아무런 할당을 안하기때문에
추상클래스는 인스턴스를 생성할 수 없는것입니다..
물론 A_Print함수도 만들필요가 없기때문에 가상함수테이블에 저장되지도 않겠죠..

 


위의 5가지만 알아도 클래스에 대해서 많은 부분을 안것입니다..
c언어로 클래스를 표현하면 클래스의 여러가지 개념에 대해 쉽게 알수 있다는사실을 알았을것입니다..
때문에 c++을 먼저공부하는것보다는 c언어 공부후 c++를 하라고 권장하는것입니다..
c언어와 c++의 차이는 불편함과 편리함의 차이지 c언어 역시 객체지향적으로 얼마든지 표현 가능하거든요..

 

클래스에 대해서 자신감이 붙었다면 디자인패턴에 대해서 공부하세요.. 디자인패턴은 클래스설계에 관한 내용이거든요..
디자인패턴을 공부해야 비로소 클래스의 진가를 알게 됩니다..

 

출처 : 네이버 지식인

http://kin.naver.com/browse/db_detail.php?d1id=1&dir_id=10104&eid=GN9zYf%2F9tkD3UgcTC2aRj4z3LGiyQnB6

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

CDatabase 을 사용한 엑셀 데이터 수정  (0) 2011.08.13
cast 연산자  (0) 2011.08.13
Byte Order  (0) 2011.08.13
ASCII Code Table  (0) 2011.08.13
ACE 란 무엇인가?  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,