C 에서의 static  
 :  C언어에서 static을 사용했던 이유는 전역변수는 프로그램어느 곳에서 접근 가능하지만, 지역변수에다가 static을 붙여주면, 지역변수가 전역변수의 특성을 지니게 하면서 접근할 수 있는 범위에 제한을 둔다는 특징이 있기 때문이었다. 

 Class, Static 멤버의 등장  
 : C++에서는 전역이라는 개념이 존재하지 않는다. 그럼 왜 C++은 왜 전역을 쓰지 않나? 기본적으로 전역변수는 접근 범위가 제한되어 있지 않기 때문에 프로그램 관리가 어려운 점도 있지만, 근본적으로 OOP에서는 전역이라는 개념이 존재하지 않기 때문이다. 그래서 전역 변수와 전역 함수를 일부 대체하기 위해서 static 멤버 라는 개념이 등장한 것이다. 
  1. #include<iostream>   
  2. using namespace std;   
  3.   
  4. int count=1;   
  5.   
  6. class Person   
  7. {   
  8.     char name[20];   
  9.     int age;   
  10. public:   
  11.     Person(char* _name, int _age)   
  12.     {   
  13.         strcpy(name, _name);   
  14.         age=_age;   
  15.         cout<<count++<<"번째 Person 객체 생성"<<endl;   
  16.     }   
  17.     void ShowData(){   
  18.         cout<<"이름: "<<name;   
  19.         cout<<"나이: "<<age;   
  20.     }   
  21. };   
  22.   
  23. int main(void)   
  24. {   
  25.     Person p1("Lee", 13);   
  26.     Person p2("Hong", 22);   
  27.   
  28.     return 0;   
  29. }  
 위의 예제는 전역 변수 count가 있고, person이 생성 될때마다 count 값을 한번씩 증가 시키고 출력시키는 프로그램이다. 생성되는 person 객체는 전역변수 count를 참조해서 값이 일정하게 증가될 것이다. 하지만 C++로 넘어 오면서 우리는, OOP, 객체지향 프로그래밍을 위하여 전역 변수, 전역 함수를 쓰지 않도록 해야 한다!!

 static 멤버의 특징  
 1. static 멤버(변수 or 함수)는 C++에서 일반적으로 클래스 변수, 클래스 함수라 불리운다. 그럼 왜 클래스 변수,함수 라 불리우는가?  객체를 생성했을 때, 객체의 멤버로 존재하는것이 아니라, 클래스 내에 선언이 되어 클래스를 기반으로 생성되는 모든 객체는 다 공유 하기 때문에(즉, 객체 단위가 아니라 클래스 단위로 사용되기 때문에) 이렇게 불리운다. 
 2. main 함수 호출 이전에 메모리 공간에 올라가서 초기화 한다. (전역변수와 동일)
 3. 선언된 클래스의 객체 내에 직접 접근을 허용한다.
 4. static 멤버 초기화문으로 초기화해야 한다. 왜 초기화문이 필요한가? 예제를 통해 알아 봅시다.
  1. #include<iostream>   
  2. using namespace std;   
  3.   
  4. class Person   
  5. {   
  6.     char name[20];   
  7.     int age;   
  8.     static int count;   
  9. public:   
  10.     Person(char* _name, int _age)   
  11.     {   
  12.         strcpy(name, _name);   
  13.         age=_age;   
  14.         cout<<count++<<"번째 Person 객체 생성"<<endl;   
  15.     }   
  16.     void ShowData(){   
  17.         cout<<"이름: "<<name;   
  18.         cout<<"나이: "<<age;   
  19.     }   
  20. };   
  21.   
  22. int Person::count=1; // static 멤버 초기화   
  23.   
  24. int main(void)   
  25. {   
  26.     Person p1("Lee", 13);   
  27.     Person p2("Hong", 22);   
  28.   
  29.     return 0;   
  30. }  

 static 멤버는 메인 함수 호출 되기 이전에 초기화가 이루어진다. 하지만 이 count를 생성자 내에서 초기화 시킬 경우에 메모리 공간에 올라감과 동시에 초기화가 이루어 지는게 아니라, 객체가 생성될때마다 값이 리셋될것이다. 그래서 항상 같은 결과만을 출력하게 되기 때문에 초기화를 위해 static 멤버 초기화문이 등장했다. 
Posted by 모과이IT
,
우선 여기서는 상속과 다형성에 대한 간단한 의미만 정리 하고 넘어갈 것입니다. 다음에 상속과 다형성에 대해서 더 자세한 포스팅을 할 것입니다. 
 상속 (Inheritance)  
 : 상속은 기존의 클래스를 토대로 해서 새로운 클래스를 만드는 방법이라고 할 수 있다. 객체지향 프로그래밍을 사용해서 워드 프로세서를 만드는 경우를 생각해보자. 고객으로부터 웹페이지 형식으로 문서를 저장할 수 있게 요구 받았다고 하면, 이때는 문서 저장과 관련된 기존의 클래스를 조금 개조해서 웹 페이지 형식으로 저장하게 만들 수 있다. 
  이렇게 기존의 클래스를 조금 고쳐서 새로운 클래스를 만들고 싶을 때 상속을 사용할 수 있다. 일상생활에서의 상속은 부모의 재산이나 부채를 그대로 이전하는 것을 의미하는데, 객체지향 프로그래밍에서의 상속도 이와 비슷하다. 클래스 A가 클래스 B를 상속받게 만들면, 클래스 B는 "부모 클래스" (or Base Class , Super Class)가 되고, 클래스 A는 "자식 클래스" (or Derived Class)가 된다. 부모 클래스는 자식 클래스에게 자신의 모든 멤버 변수와 함수를 물려준다. 물론 OOP에서는 물질적인 것 뿐만 아니라, 어떤 기본적인 사람의 특성들도 상속의 특성이 될 수 있다. (아래는 상속을 사용한 클래스의 간단한 예제)
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person   
  5. {   
  6.     int age;   
  7.     char name[20];   
  8. public:   
  9.   
  10.     int GetAge() const {   
  11.         return age;   
  12.     }   
  13.     const char* GetName() const {   
  14.         return name;   
  15.     }   
  16.   
  17.     Person(int _age=1, char* _name="noname"){   
  18.         age=_age;   
  19.         strcpy(name, _name);   
  20.     }   
  21. };   
  22.   
  23. class Student: public Person   
  24. {   
  25.     char major[20]; //전공   
  26. public:   
  27.     Student(char* _major){   
  28.         strcpy(major, _major);   
  29.     }   
  30.     const char* GetMajor() const {   
  31.         return major;   
  32.     }   
  33.     void ShowData() const {   
  34.         cout<<"이름: "<<GetName()<<endl;   
  35.         cout<<"나이: "<<GetAge()<<endl;   
  36.         cout<<"전공: "<<GetMajor()<<endl;   
  37.     }   
  38. };   
  39.   
  40. int main(void)   
  41. {   
  42.     Student Kim("computer");   
  43.     Kim.ShowData();   
  44.   
  45.     return 0;   
  46. };  
 상속받는 클래스를 만들기 위해서 class Student : public [상속할 클래스 이름] 이런식으로 상속이라는 문법을 사용할 수 있다. 상속을 사용한 프로그램은 메모리 공간 할당 -> 부모 클래스의 생성자 실행 -> 자식 클래스의 생성자 실행 이라는 순서를 가지게 된다.

 다형성 (Polymorphism)  
 : 다형성(Polymorphism)은 OOP(Object Oriented Programming)의 개념을 설명할 때 추상화(Abstraction)과 더불어 가장 중요하게 등장하는 용어이다. 다형성을 이해하기 위해 간단한 예를 들어 보도록 하자. 예를 들어 돈을 생각해보자. 돈 만원은 만원짜리 한장, 천원짜리 열장, 백원짜리 동전 100개로도 구성 할 수 있다. 즉 만원을 구성하는 형태는 다 틀리지만, 모두 동일한 가치인 만원을 의미하게 된다. 이렇게 만원을 다양한 형태로 나타낼 수 있는 것이 다형성이다. 
  객체지향 언어에서 의미하는 다형성은 다음과 같은 의미로 정의할 수 있다. 
서로 다른 객체가 동일한 메시지에 대하여 서로 다른 방법으로 응답할 수 있는 기능
여기서 "서로 다른 객체"는 서로 다른 클래스를 의미한다. 물론 상속의 경우에도 해당이 된다. 다음의 "동일한 메시지"라는 의미는 서로 다른 객체에게 같은 메세지를 보낸다는 의미가 되겠다. 마지막으로 "서로 다른 방법"으로 응답한다는 의미는 무엇일까? 
 예를 들어 보자. 우선 어떤 도형 모형의 클래스와 이 클래스를 상속 받는 삼각형, 사각형, 원형... 등의 이런 형태를 갖는 클래스가 있다고 하자. "서로 다른 객체"는 삼각형, 사각형에서 각자 생성한 객체가 될것이고,  "동일한 메시지"는 도형을 그려라 라는 의미를 가지는 메소드인 Draw 함수가 될것이다. 그렇다면 "서로 다른 방법"은 draw라는 같은 형태의 메소를 받아 삼각형, 사각형 객체는 삼각형, 사각형을 그릴텐데, 삼각형을 그리기 위한 방법과 사각형을 그리기 위한 방법. 이것들은 서로 다를 것이다. 즉, 같은 메소드 호출에 대해 서로 다른 방법으로 응답을 하게 되는 것이다. 
 위와 같은 개념을 객체지향에서 다형성이라고 한다. 위와 같은 계층 구조를 그림으로 나타내면 아래와 같다. 

Figure 클래스는 하위 클래스에서 모형을 그리는데 사용될 수 있는 draw 함수를 가지고 있다. 그러나 Figure 클래스에는 실제 도형을 그리는 함수 구현 부분을 정의할 수가 없다. 그 이유는 Figure의 하위 클래스인 Triangle, Square, Circle 클래스마다 그리는 방법이 다르기 때문이다. 객체지향에서는 이러한 경우 Figure Class를 추상 클래스(Abstract class)로 정의 하고 draw 함수 역시 추상 함수(메소드)로 정의한다. 
 추상 메소드(함수)란 함수의 선언부분만 있고 구현 부분이 업스는 함수를 말한다. 이렇게 선언된 추상 클래스는 하위 클래스에서 구현되어 사용된다. 즉, 각각의 하위 클래스에서 상속받은 추상 메소드를 서로 다른 방법으로 구현하게 되는 것이다. 이를 코드로 정리 해보면 아래와 같은 코드가 나올 것이다. 
  1. #include <iostream>   
  2. #include <string>   
  3. using namespace std;   
  4.   
  5. class Figure   
  6. {   
  7. public:   
  8.     virtual string draw() = 0;     
  9. };   
  10.   
  11. class Triangle : public Figure   
  12. {   
  13. public:   
  14.     virtual string draw() { return "Draw Triangle"; }   
  15. };   
  16.   
  17. class Square : public Figure   
  18. {   
  19. public:   
  20.     virtual string draw() { return "Draw Square"; }   
  21. };   
  22.   
  23. class Circle : public Figure   
  24. {   
  25. public:   
  26.     virtual string draw() { return "Draw Circle"; }   
  27. };   
  28.   
  29. int main()   
  30. {   
  31.     Figure* F1 = new Triangle;   
  32.     Figure* F2 = new Square;   
  33.     Figure* F3 = new Circle;   
  34.   
  35.     cout << F1->draw() <<endl;   
  36.     cout << F2->draw() <<endl;   
  37.     cout << F3->draw() <<endl;   
  38.        
  39.     delete F1;   
  40.     delete F2;   
  41.     delete F3;   
  42.            
  43.     return 0;   
  44. }  

Posted by 모과이IT
,
우선 다음의 상속이 구현된 소스 코드를 보고 문제점이 무엇인지 알아 보자. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person   
  5. {   
  6.     int age;   
  7.     char name[20];   
  8. public:   
  9.   
  10.     int GetAge() const {   
  11.         return age;   
  12.     }   
  13.     const char* GetName() const {   
  14.         return name;   
  15.     }   
  16.   
  17.     Person(int _age=1, char* _name="noname"){   
  18.         age=_age;   
  19.         strcpy(name, _name);   
  20.     }   
  21. };   
  22.   
  23. class Student: public Person   
  24. {   
  25.     char major[20]; //전공   
  26. public:   
  27.     Student(char* _major){   
  28.         strcpy(major, _major);   
  29.     }   
  30.     const char* GetMajor() const {   
  31.         return major;   
  32.     }   
  33.     void ShowData() const {   
  34.         cout<<"이름: "<<GetName()<<endl;   
  35.         cout<<"나이: "<<GetAge()<<endl;   
  36.         cout<<"전공: "<<GetMajor()<<endl;   
  37.     }   
  38. };   
  39.   
  40. int main(void)   
  41. {   
  42.     Student Kim("computer");   
  43.     Kim.ShowData();   
  44.   
  45.     return 0;   
  46. };  
 위의 소스 코드 문제점을 보자면 main에서 객체 생성을 하는 Student Kim 객체선언 하는것을 보아 이름이 Kim으로 예상할 수 있겠다. 하지만 여기서 나이와 전공은 우리가 원하는 형태로 초기화 하지 못한다는 점이 바로 문제점이다. 즉, Student 클래스 객체가 생성될때, 자신의 멤버는 생성자내에서 초기화 하고 있지만, Person 클래스의 멤버는 default 값으로 초기화 되고 있는게 문제라고 할 수 있다. 
 잠깐 소스를 살펴 보자면, showdata 함수 내에서 GetName이라는 함수를 호출 하고 있는데, Student 클래스내에서는 GetName이라는 함수가 없지만, 이것을 호출 할 수 있는 이유는 Student 클래스가 Person 클래스를 상속하고 있기 때문에 Person 에 GetName 이라는 함수가 있어서 이런 호출이 가능한 것이다. (상속의 특성이라 할 수 있다.)

 문제로 돌아와서, Student 클래스에 Person의 멤버 변수들도 Student 클래스의 멤버로 상속되어 지니까 아예 Student 클래스를 정의 할때, 인자값으로 나이와 이름을 초기화 해서 Default로 초기화 되는것을 개선해 보자는 것이다.  
  1. Student (int _age, char* _name , char * _major)   
  2. {   
  3.     age = _age;   
  4.     strcpy(name, _name);   
  5.     strcpy(major, _major);   
  6. }  
 위와 같이 Student 생성자에서 age와 name을 초기화 해주면 될것이다. 하지만 Person 멤버들이 private 으로 선언되어 있으니 위와 같이 코딩을 하면 컴파일 에러가 날것이다. 왜 이런 에러가 나는 것일까? 비록 Person 클래스의 멤버는 Student 클래스에 의해 상속 되어 지지만, Person 클래스의 멤버들이 private이기 때문에 Person 클래스 내에서만 접근 가능 하기 때문이다. 
 그렇다고 이 문제를 해결하기 위해 public으로 멤버 변수들을 선언하면 문제는 해결되겠지만, 객체지향의 정보은닉이 무너지는 결과를 낳을 것이다. 그래서 필요한게 바로 멤버 이니셜라이저(member initializer)이다. (또는 초기화 리스트)

 멤버 이니셜라이저  
  1. Student(int _age, char* _name, char* _major)   
  2.     : Person(_age, _name)   
  3. {   
  4.     strcpy(major, _major);   
  5. }  
 보통 멤버 이니셜라이저에서는 멤버 변수의 초기화를 이루기 위해 멤버변수 이름이 오지만, 여기에서는 클래스 이름이 나왔다. 이 의미는 _age와 _name 이 두개의 인자값을 받을 수 있는 생성자를 호출하라는 의미로 바뀐다. 이제 완성된 코드로 실행해보자. 이제 원하는 대로 출력이 되는 것을 알 수 있다. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person   
  5. {   
  6.     int age;   
  7.     char name[20];   
  8. public:   
  9.   
  10.     int GetAge() const {   
  11.         return age;   
  12.     }   
  13.     const char* GetName() const {   
  14.         return name;   
  15.     }   
  16.   
  17.     Person(int _age=1, char* _name="noname"){   
  18.         age=_age;   
  19.         strcpy(name, _name);   
  20.     }   
  21. };   
  22.   
  23. class Student: public Person   
  24. {   
  25.     char major[20]; //전공   
  26. public:   
  27.     Student(int _age, char* _name, char* _major)   
  28.         : Person(_age, _name)   
  29.     {   
  30.         strcpy(major, _major);   
  31.     }   
  32.     const char* GetMajor() const {   
  33.         return major;   
  34.     }   
  35.     void ShowData() const {   
  36.         cout<<"이름: "<<GetName()<<endl;   
  37.         cout<<"나이: "<<GetAge()<<endl;   
  38.         cout<<"전공: "<<GetMajor()<<endl;   
  39.     }   
  40. };   
  41.   
  42. int main(void)   
  43. {   
  44.     Student Pac(19, "Pacs.tistory.com""computer");   
  45.     Pac.ShowData();   
  46.   
  47.     return 0;   
  48. };  


 protected   
 : protected는 접근제어 키워드 중 하나로, 정보은닉 포스팅에서 이미 언급한 바가 있는데, protected는 private과 기능이 완전히 동일 하지만, 이것이 상속관계에서 쓰일 때는 파생 클래스가 기본 클래스로의 접근이 가능해진다. 
  1. class Person   
  2. {   
  3. protected:   
  4.     int age;   
  5.     char name[20];   
  6. public:   
  7.       .................   
  8. };  
 사용할 때는 위와 같이 private 대신에 protected를 써주면 되겠다. 하지만 protected를 쓴다고 해도 멤버 이니셜라이저를 쓰는것이 좋은 구조이다. 만약 멤버 변수의 이름이 변경되어 지는 경우가 생겼다고 한다면, 이 바뀐 이름들을 자신 클래스는 물론이고, 상속을 하고 있는 클래스에서도 이름을 바꿔야 하는 문제가 생긴다.  단순히 이름을 바꾸면 된다고 생각하기 쉽지만, 한클래스의 변경은 다른 클래스의 변경을 유발 시켰기 때문에 심각한 문제라고 볼 수 있다. 
  
 ※ Derived class 생성자 내에서는 Derived class의 멤버 변수만 직접적으로 초기화 시켜 주고 Base class 멤버는 Base class의 생성자를 통한 초기화가 가장 좋은 방법이라고 말할 수 있다. 
Posted by 모과이IT
,
1. C언어에서의 구조체  

  : 구조체란 다른 타입을 가지는 변수들을 하나의 이름으로 묶어둔 것이라고 할 수 있다. 그럼 구조체를 왜 사용할까? 구조체는 관련있는 데이터를 하나의 자료형으로 묶어서, 프로그램의 구현 및 관리가 용이 해진다는 장점을 가지고 있기 때문이다.

  C언어에서 구조체에 대한 불만이라고 한다면, 구조체를 기본 자료형으로 인식해 주지 않는다는 점이다. 아래 예제를 보자. 
  1. //Person이라는 사용자 정의 자료형 (Structure)   
  2. struct Person{   
  3.     int age;   
  4.     char name[10];   
  5. };    
  6.   
  7. int main()   
  8. {   
  9.     int a =10;   
  10.     Person P //C에서는 에러가 난다.    
  11.        
  12.     return 0;   
  13. }  
2. C++에 와서 달라진점?  

   C++로 넘어오면서 구조체 사용에 대해 달라진점이 있다고 하면, C에서는 구조체에 함수를 넣을 수 없었지만 C++에서는 넣을 수 있다는이고, 위의 예제와 같이 C에서는 구조체 사용을 위해서는  Person P 라고 사용하면 안된다.struct Person P 이렇게 써야 한다. C++은 기본 자료형이나 사용자 정의 자료형이나 다 동일한 자료형으로 인식함으로써 이와 같은 문법 사용이 이루어진 것이다.  또한, C에서는 사용자 정의 자료형을 가지고 +, -, * 이런 연산이 허용안되지만, C++에서 연산자 오버로딩을 통해 이런것까지도 이런것도 가능하다. 


3. 기본자료형과 사용자 정의 자료형  

 - C에서의 기본 자료형 : int , char, float.... 

               사용자 정의 자료형 : 구조체, 공용체(Union), enum

- C++ 에서의 기본 자료형 : C와 동일
                   사용자 정의 자료형 : C와 동일 하고 여기에 Class가 추가 되었다. 

4. 클래스  

  :  변수와 함수를 하나로 묶어서 자료형을 정의 할 수 있는 문법적 요소 (변수 + 함수) C++에서는 구조체도 클래스의 범주에 들어가며, 클래스의 변수는 변수라고 하지 않고 객체라고 불리운다. 그럼 왜 클래스란 개념이 생겨났는가?


5. 왜 클래스란 개념이 등장했는가?  

   클래스를 설명 하기 앞서서 잠깐 객체지향을 언급해야 겠는데 객체지향이란 프로그램을 구현하는데 있어서 현실세계를 그대로 모델링을 해서 이것을 그대로 프로그래밍으로 옮겼으면 좋겠다는 생각을 모티브로 나온 것이다. 바로 현실세계에 존재하는 대상을 가지고, 자료형으로 정의 하는것이다. 하지만 현실세계의 사물들을 자료형으로 정의 하기 위해서는 데이터 추상화가 필요 한데, 데이터 추상화란 현실세계의 사물을 데이터적인 측면과 기능적인 측면을 통해서 정의 한것이라 할 수 있다. 

  이런 데이터 추상화를 거쳐서 클래스화 되고 인스턴스화(객체를 만들어내는 행위 : 일명 객체화)가 일어 난다. 

* 그럼 왜 클래스는 함수와 변수로 표현해야 될까?
 : 현실세계에 존재하는 대상을 추상화 하다 보면, 데이터적 특성도 있지만, 기능적인 측면이 있기 때문에  함수 변수 이 두개를 하나의 자료형으로 묶는 것이다. 이것이 바로 클래스이다.
Posted by 모과이IT
,
다음과 같이 상속(Inheritance)의 경우를 보자 
  1. class A : public B   
  2. {   
  3. };  
 B클래스를 public으로 상속하겠다는 의미로 우리들이 일반적으로 많이 사용하는 class A : [상속형태] B 형태를 가지고 있다. 기본적으로 기본 클래스는 파생 클래스에 의해 상속되어지는 과정에서 접근 권한이 변경이 된다. 그 상속 형태에 따라 멤버들을 상속하는데 있어서 접근권한을 무엇으로 변경할 것이냐 하는 얘기이다. 아래는 기본 클래스와 상속의 형태에 따른 파생클래스에서의 기본클래스로의 접근범위를 표로 나타낸것이다. 일반적으로 public을 가장 많이 쓰이므로 그 부분에 초점을 맞춰서 보는 것이 좋다.

그냥 도표로는 언뜻 이해 하기가 힘드니, 이것을 코드와 분석을 통해서 더 자세히 알아 보자.(상속의 형태는 매번 볼때마다 헷갈리니 잘 알아 두도록 하자)
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Base{   
  5. private:   
  6.     int a;   
  7. protected:   
  8.     int b;   
  9. public:   
  10.     int c;   
  11. };   
  12.   
  13. class Derived : public Base //or protected Base or private Base   
  14. {   
  15.   
  16. };   
  17.   
  18. void main()   
  19. {   
  20.     Derived obj;       
  21. }  
 protected 상속의 경우  
: protected Base' 로 선언 했다고 가정하자. 상속의 형태가 protected이다. 이 의미는 protected보다 접근 권한이 넓은 것이 있다면, 그냥 protected에 맞춰서 내가 상속을 하겠다는 의미를 가지고 있다. 
 - int c : 상속되어지는 과정에서 int c는 지금 public 인데 protected보다 접근할 수 있는 범위가 넓기 때문에 상속이 되면, protected로 맞춰지게 된다. 
 - int b : 여전히 protected가 된다. 
 - int a : private인 int a는 private는 protected 보다 접근 권한이 작으니까 그대로 private로 상속되겠다 생각하기 쉽지만, 파생 클래스에서 int a는 private int a로 선언이 된다. (상속되면 결과적으로) int a는 private이기 때문에 파생 클래스에서 접근 가능한것으로 생각하기 쉽지만, 접근가능하지 않다 그 이유는 private 멤버의 특성상 자기 클래스 내에서만 접근 가능하지 상속됐다고 파생클래스에서 접근 하지는 못하기 때문이다. 그래서 우리는 헷갈리지 않게 Base 클래스에서 private로 선언된 멤버는 상속이 될때 접근불가 라는 키워드가 붙는다고 생각하는 것이 좋다.

 private 상속의 경우  

 - int c , int b: 이 둘은 private보다 범위가 넓으니 private로 맞춰 지겠다.
 - int a : 위에서 언급한 Base 클래스에서 private로 선언된 멤버는 접근불가라는 것을 기억하자.

 public 상속의 경우  

 - int c : 범위가 같으므로 같은 public 으로
 - int b : public 보다 범위가 작으니 자신 그대로의 접근범위를 가져온다. protected
 - int a :  위에서 언급한 Base 클래스에서 private로 선언된 멤버는 접근불가라는 것을 기억하자.
Posted by 모과이IT
,
is-a 관계  
 : 상속에 있어서 우리는 주로 public 상속에 대해서 이야기 할 것이고, 사용할 것이다. 그럼 우리가 주로 사용할 puiblic 상속을 사용할때는 is-a관계가 성립되도록 만들어야 한다. 그럼 is-a 관계는 무슨 관계이냐? 말그대로 is a : ~은 ~이다 라는 관계를 성립하자는 이야기이다.
 예를 들어 사람과 학생의 관계를 생각해보자 (Person - Student) 
 - Person is a student  : 사람은 학생이다. 이것은 성립이 안된다. 모든 사람이 학생일수는 없는 노릇 아닌가?
 - Student is a person  : 학생은 사람이다. 이것은 성립된다. 그래서 우리가 상속 클래스를 만들때, Student 클래스는 파생클래스로 Person 클래스를 상속할 수 있는것이다. 

* 왜 is-a 관계여야 하나? 

 위와 같은 상속 관계에서 클래스는 모든 사람의 공통적인 분모를 가지고 추상화 시켜서 클래스를 만들것이다. 각각의 클래스의 범위는 모든 사람, 모든 학생, 모든 장학생으로 범위가 잡혀질 것이다. 일반적으로 사람이 10명있을 때보다, 5명 있을때(즉, 인원수가 적을수록) 우리는 더많은 공통적인 특성을 더 많이 뽑아 낼 수 있다. 
 위의 그림을 보면, 아래로 내려 갈수록 범위는 좁아지겠지만, 기능은 많아질 것이다. 범위가 좁아지기 위해서는 is-a 관계로 형성이 되어야 한다. 기능이 많아지면 멤버 함수가 늘어 난다는 의미인데 그것이 C++의 상속적 특성과 완전히 일치한다. 그래서 is-a관계를 성립시키는 것이 상속을 하는 조건에 있어서 반드시 필요하다. 
상속관계가 있을 때, 아래로 갈수록 구체화 또는 특수화(specialization) 되어 지고, 

                                                                                  올라갈수록 보다 일반화(generalization) 되어 진다. 
 정리 하자면, is-a관계가 성립될 때, 아래로 갈수록 특성이 많아 지는데 이러한 특성들은 멤버 함수와 멤버 변수로서 반영이 되고, 그렇기 때문에 상속으로서 관계를 맺어 줄 수 있는 것이다. 상속 관계에 있을 때 파생클래스는 기본클래스의 모든 특성들을 포함하기 때문에(특성들이 많아 지기 때문에) is-a 관계로 표현한다.

 is-a 관계의 잘못된 상속 예  


 아까 위에서 성립이 안된다던 Person is a student의 is-a 관계를 예로 한번 들어 보자. 만약 Student 클래스에서 공부하는 기능인 study함수가 있고 Person 클래스가 Student 클래스를 상속 했다고 하자. main 에서 Person 객체를 여러개 생성했다고 하면, Person은 상속받은 함수의 기능을 가지고 있으므로, 모두 study 기능을 가지고 있을 것이다. 하지만, 모든 사람이 공부를 하는 것이 아닐것이다. 
  그럼에도 불구하고 스터디 기능을 다 가지고 있을 것이다. 왜냐하면 우리가 프로그램에서 상속관계를 이렇게 묶어 냈기 때문이다. 그래서 문제가 된다. 프로그램도 현실세계에 있는 기능을 그대로 프로그래밍언어로 표현한 것이기 때문에 현실세계와 동떨어지면 그 프로그램은 난해한 프로그램이 되는 것이다. 따라서 is-a 관계를 성립 되도록 만들어야 한다. 

 Has-a 관계  
 : is-a 관계과 마찬가지로 ~ has a ~ : ~가 ~을 소유한다. 이런 의미를 가지는 관계를 말한다. 예를 들어 보면 이런거다.  
 - Policeman have a cudgel  : 경찰은 몽둥이를 소유한다. 
  1.   
  2. #include <iostream>   
  3. using namespace std;   
  4.   
  5. class Cudgel  //몽둥이   
  6. {    
  7. public:   
  8.     void Swing(){ cout<<"Swing a cudgel!"<<endl; }   
  9. };   
  10.   
  11. class Police : public Cudgel  //몽둥이를 소유하는 경찰   
  12. {   
  13. public:   
  14.     void UseWeapon(){ Swing(); }   
  15. };   
  16.   
  17. int main()   
  18. {   
  19.     Police pol;   
  20.     pol.UseWeapon();   
  21.   
  22.     return 0;   
  23. }  
 위에서 몽둥이 클래스는 휘두르는 기능인 Swing 함수를 가지고 있다. 경찰 클래스는 몽둥이 클래스를 상속했기에 몽둥이 클래스의 기능을 사용할 수 있다.(즉, 소유한 것이다) 이것으로 "Police is cudgel"의 is-a 관계는 성립이 안되지만, has-a 관계가 성립되어도 상속이 된다는것을 알 수 있다. 

 Has-a 관계에의한 상속 & 포함  
 : has-a 관계는 상속뿐만이 아니라, 다른 방법에 의해서도 표현가능하다. 그것이 바로 포함이라는 개념이다. 그 첫번째는 "객체 멤버에 의한 포함관계" 이다. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Cudgel  //몽둥이   
  5. {    
  6. public:   
  7.     void Swing(){ cout<<"Swing a cudgel!"<<endl; }   
  8. };   
  9.   
  10.   
  11. class Police //몽둥이를 소유하는 경찰   
  12. {   
  13.     Cudgel cud;   
  14. public:   
  15.     void UseWeapon(){ cud.Swing(); }   
  16. };   
  17.   
  18. int main()   
  19. {   
  20.     Police Pol;   
  21.     Pol.UseWeapon();   
  22.   
  23.     return 0;   
  24. }  
 Police Class의 멤버 변수로 Cudgel class의 객체가 왔다. (C에서의 구조체 멤버로 구조체 변수가 올수 있는것과 같은 이치) C++은 기본 자료형이나 사용자 정의 자료형이나 동일시 하고 있는 특성을 가지고 있다. 그러기에 이런 선언이 가능한 것이다. 

 일단 메인함수에서 클래스의 객체로 만들때 어떻게 만들어 지는지 알아 보자.
 처음에 pol이라는 이름으로 메모리 공간을 할당하고, cudgel 객체를 위한 메모리 공간도 할당을 해줄 것이다. 이때의 순서를 정리 하자면  Police 메모리 공간 할당 -> cudgel 객체 메모리 공간할당 & 생성자 호출 -> Police 생성자 호출 이 이뤄질 것이다. 

 13번째 줄인 Cudgel cud; 는 명시적으로 어떤 생성자를 호출 하라고 명시 되어 있지 않으므로, Cudgel 클래스의 인자값 받지 않는 void 생성자를 호출 할 것이다.  이러한 형태로 클래스의 객체가 멤버로 포함되기 위해서는 반드시 인자값 받지 않는 void 생성자를 지녀야 한다. 

 여기서 13번째 줄에 Cudgel cud(10); 이라는 문장도 쓸수 있지 않을까? 라는 의구심을 가지게 된다. "cud 생성과정에서 10이라는 인자값을 받는 생성자를 호출 하겠다"라는 의미이다. 이런 문장이 올 수 있을 거 같지만, 오지 못한다. 이 문장은 객체를 생성과 동시에 초기화하는 효과를 가지는데, C에서 구조체 멤버 변수 선언과 동시에 초기화 불가능한 것과 마찬가지로 C++에서도 클래스의 멤버는 생성과 동시에 초기화가 불가능하다. (C/C++ 이외에 java 와 C#은 가능하다) 따라서, C++은 클래스의 객체가 멤버로 존재하기 위해서는 그 객체를 기반으로 하는 클래스는 반드시 void 생성자를 지녀야 한다. 

 결과적으로 생성된 police 객체에는 cud라는 객체가 존재하는 것이다. police는 cud를 포함하고 있는 것이다. 그렇기 때문에 UseWeapon 함수를 호출하면서, 자기가 가지고 있는 Swing 함수를 호출 할 수 있는 것이다. 이것을 포함관계라고 하고, UML에서는 아래와 같이 표현한다. 

 객체 포인터 멤버에 의한 포함관계  
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Cudgel  //몽둥이   
  5. {    
  6. public:   
  7.     void Swing(){ cout<<"Swing a cudgel!"<<endl; }   
  8. };   
  9.   
  10.   
  11. class Police //몽둥이를 소유하는 경찰   
  12. {   
  13.     Cudgel* cud;   
  14. public:   
  15.     Police(){   
  16.         cud=new Cudgel;  //객체 동적 생성 (void 생성자 호출) 인자값받는 객체 동적 생성도 가능   
  17.     }   
  18.     ~Police(){   
  19.         delete cud;   
  20.     }   
  21.     void UseWeapon(){ cud->Swing(); }   
  22. };   
  23.   
  24. int main()   
  25. {   
  26.     Police pol;   
  27.     pol.UseWeapon();   
  28.   
  29.     return 0;   
  30. }  
 위 예제는, 객체 포인터 멤버에 의한 포함관계의 예제이다. 일단 그림을 한번 보자.

 소스코드 13번째 줄에서, cudgel 객체가 생성되고,그 포인터를 cud라는 멤버에 대입을 하니까 police 객체는 cud 객체를 위와 같이 가리키는 형태를 띄게 될것이다. 그렇기 때문에, UseWepon 함수에서는 cud라는 포인터가 가리키는 객체의 swing 함수를 호출 할 수가 있다.  
  객체는 동적 생성했으므로, cudgel 객체는 힙(Heap)에 있을 것이고 메인함수에서 생성된 지역적인 특성을 지니는 객체 이므로 cud는 스택(Stack)에 존재할 것이다. 
 물론 존재하는 위치 / 메모리 구조가 틀리지만, 결과적으로 이것도 포함 관계라고 할 수 있다. 여기서 말하는 포함은 물리적인 특성이 아니라, 논리적, 기능적인 특성을 같이 하고 있는 것도 포함이라고 할 수 있다.
 보시다시피 논리적으로 Police 클래스 Cudgel 객체의 포인터를 가지고 있으므로 그것을 소유 하고 있는것이다. (언제든 참조 가능) 이것도 역시 포함 관계에 해당된다. 위의 형태를 가진것이 바로 객체 포인터 멤버에 의한 포함관계이다. (주로 이것을 많이 쓴다)

 상속으로 묶인 두 개의 클래스는 강한 연관성(결합도(coupling))을 띄게 된다. 만약 소유관계의 상속에서 경찰이 몽둥이를 가지지 않은 경찰을 표현하기 위해서는 클래스의 구조 자체가 바뀌어야 한다. 하지만 이를 포함관계로 구성하면 간단히 표현이 가능해진다. 따라서, 상속은 Is-a관계의 표현에 매우 적절하다. 그리고 경우에 따라서는 Has-A 관계의 표현에도 사용될 수 있으나, 이는 프로그램의 변경에 많은 제약을 가져다 줄 수 있다.
Posted by 모과이IT
,
객체 포인터  
 : 객체 포인터란 객체를 가리킬 수 있는 포인터를 의미한다. (객체의 주소 값을 저장할 수 있는 포인터)
  - 예를 들어 A 클래스의 포인터는 A 객체뿐만 아니라, A클래스를 상속하는 파생 클래스 객체의 주소 값도 저장이 가능하다

 다음 상속 관계를 한번 살펴 보도록 하자.
 is-a관계에 의해서 ScholarStd 객체는 Student 객체이자 Person 객체도 동시에 된다.  ("ScholarStd 는 Person 객체이다." 이런 말도 틀린말은 아니다) 그러면 "Student 객체는 ScholarStd  객체이다" 는 성립이 안된다. is-a 관계는 아래쪽으로 성립이 안된다. 우리가 프로그램상에서 서로의 객체를 생성했다고 해보자. 아래의 예제는 is-a 관계에서 위의 그림은 소스코드로 나타낸 것이다. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person   
  5. {   
  6. public:   
  7.     void Sleep(){    
  8.         cout<<"Sleep"<<endl;   
  9.     }   
  10. };   
  11.   
  12. class Student : public Person   
  13. {   
  14. public:   
  15.     void Study(){   
  16.         cout<<"Study"<<endl;   
  17.     }   
  18. };   
  19.   
  20. class ScholarStd  : public Student   
  21. {   
  22. public:   
  23.     void Receive_Scholar(){   
  24.         cout<<"Work"<<endl;   
  25.     }   
  26. };   
  27.   
  28. int main(void)   
  29. {   
  30.     Person* p1=new Person;   
  31.     Person* p2=new Student;   
  32.     Person* p3=new ScholarStd ;   
  33.   
  34.     p1->Sleep();   
  35.     p2->Sleep();   
  36.     p3->Sleep();   
  37.   
  38.     return 0;   
  39. }  
여기서 p1,2,3 포인터는 Person 클래스의 포인터니까 Person클래스의 객체를 가리킬 수 있는 포인터가 되는것이다. (*p1는 Person 클래스의 객체를 가리킬 수 있는 포인터) 
- p2라는 포인터는 Student 클래스의 객체를 가리키는 것도 문제가 없을 것이다. 
- p3라는 포인터는 ScholarStd 클래스의 객체를 가리키는 것도 문제가 없을것이다. 

그럼 "Student *s;" 라고 선언할시를 생각해 보자. 이 선언은 ScholarStd 클래스의 객체를 가리키는 것은 문제 없다. (ScholarStd 객체는 Student 객체이자 Person 객체이므로) 하지만 포인터 s는 Person 클래스의 객체를 가리키는 것은 문제가 된다. 왜냐하면 모든 Person 객체들이 다 Student가 아니기 때문이다. 그리고 클래스의 객체를 가리킨다는 의미는 역으로 is-a관계가 성립해야 된다는 걸 의미한다.

 객체 포인터 권한  
  지금까지는 객체가 가리키는 것이 어떤 경우가 합당한지를 알아 봤는데, 이 객체를 가지고 그 안의 함수를 사용하는 경우는 어떨까? 객체 포인터 권한에 대해 알아보자. 
  예를 들어 여기 A라는 클래스가 있다 A는 B에 의해 상속되어 지고, C는 B클래스에 의해 상속되어 진다고 가정하고 각각의 클래스에는 클래스 이름에 맞는 a,b,c() 함수가 존재한다고 해보자. 
 그 후, C 클래스의 객체를 생성한다고 하자. 그러면 C클래스의 객체 안에는 아마도, A클래스내에 선언되어 있는 a()함수도 있을것도 b()도 있고 자신의 클래스의 함수에 c()도 있을것이다. 아래의 그림 처럼 말이다. (아래의 그림처럼 함수 경계선이 있는것은 아니다.) C 클래스의 객체는 B객체이자 A 객체가 되므로 A,B,C 클래스의 포인터를 가지고 이 것을 가리킬수 있을 것이다. 
  이렇게 포인트 변수들이 가리키는 대상이 같을 경우, 만약 포인터 A (a*)가 가리키는 C 객체의 주소값이 0x10번지 라고 하면, 포인터 b,c의 주소값은 0x10번지로 으로 다 똑같다.  
  A클래스의 포인터를 가지고 가리키는 대상이 비록 C객체 이지만, A클래스의 포인터를 가지고 호출 할 수 있는 함수는 A클래스 내에 선언되어 있는 멤버 변수나 멤버함수로 제한적이다.  "A클래스 포인터(A* a) 가 가리키는 대상이 C 객체 일지라도, A 클래스의 포인터로 접근 할 수 있는 영역은 A 클래스내로 제한된다." 라는 이야기 이다.  


 위의 이야기를 염두에 두고 소스코드를 보자.  C클래스의 포인터 c는 C클래스 객체를 가리키고 있다. 그러므로 C클래스의 포인터로 접근할 수 있는 영역은 C클래스내로 제환된다는 이야기 이다. is-a관계에 의해 C는 모든 함수에 접근 권한을 가지게 되므로 아래의 코드는 문제가 없다. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class A{   
  5. public:   
  6.     void a(){ cout<<"call a function"<<endl; }   
  7. };   
  8.   
  9. class B : public A   
  10. {   
  11. public:   
  12.     void b(){ cout<<"call b function"<<endl; }   
  13. };   
  14.   
  15. class C : public B   
  16. {   
  17. public:   
  18.     void c() { cout<<"call c function"<<endl; }   
  19. };   
  20.   
  21. void main()   
  22. {   
  23.     C* c = new C;   
  24.     c->a();   
  25.     c->b();   
  26.     c->c();   
  27. }  

다음의 코드를 한번 살펴보자. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class A{   
  5. public:   
  6.     void a(){ cout<<"call a function"<<endl; }   
  7. };   
  8.   
  9. class B : public A   
  10. {   
  11. public:   
  12.     void b(){ cout<<"call b function"<<endl; }   
  13. };   
  14.   
  15. class C : public B   
  16. {   
  17. public:   
  18.     void c() { cout<<"call c function"<<endl; }   
  19. };   
  20.   
  21. void main()   
  22. {   
  23.     C* c = new C;   
  24.     B* b = c;    
  25.     A* a = b;   
  26.   
  27.     cout<<c <<endl;   
  28.     cout<<b <<endl;   
  29.     cout<<a <<endl;   
  30. }  


 우선 24번째 줄에 있는 B* b = c;  문법을 보자. 여기서 우리는 포인터의 타입이 일치 하지 않는데 어떻게 이런 문법이 가능한가 라는 의구심을 가지게 될것이다. 하지만 앞서 배웠듯이, C클래스의 포인터는 C클래스의 포인터 이자, A,B 클래스의 포인터도 되므로 이런 문법은 성립이 되는 것이다. 
 문제 이야기로 넘어와 보자. 위 예제는 객체 포인터 권한 도입부분에 설명한 부분을 코드로 풀어 놓은 것이다. c,b,a를 출력해보면 같은 주소값을 가짐을 확인할 수 있다. 
  그리고 또 확인해야 할 것이 있다. B클래스의 포인터를 이용해 각각 함수를 호출해 보자. 메인함수에 아래와 같은 코드를 넣어 컴파일해 보자.
  1. b->a();   
  2. b->b();   
  3. b->c(); //에러  

위와 같이 컴파일 에러를 확인해 볼수 있다. 왜그런 것일까? 우리는 b라는 포인터가 가리키는 객체가 C클래스의 객체라는 것을 알고 있지만, 컴파일러는 b라는 포인터가 가리키는 대상이 C클래스의 객체라는것을 모른다. (그게 C건 어떤 것이건 아예 모른다.) b라는 포인터가 어느 클래스의 객체를 가리키는지는 runtime에 결정 되기 때문에 컴파일러는 이 대상이 어느 객체를 가리키는지 결정을 못내리는 것이다.

 이와 마찬가지로 A의 클래스 포인터 a 가 가리키는 것이 우리는 C클래스의 객체라는것을 알고 있지만, 컴파일러는 무엇을 가리키는지 알지 못한다. 그래서 컴파일러는 포인터 a가 가리키는 대상이 무엇이든지 a함수의 호출은 전혀 문제가 안될것이라는 판단하에 a함수 호출만을 보장하는 것이다. 
Posted by 모과이IT
,

object님 블로그에서 VC++6을 쓰지 말아야하는 이유 라는 글을 올려주셨는데, VC2008에서도 비슷하게 적용되는 부분이 있고 마침 관련된 세미나도 한번 진행 해서 한번 작성해 봤다.

 

1.     보다 안전한 프로그래밍

SCL, CRL등 라이브러리의 거의 모든 함수와 API SAL이 적용이 되었다.

이번에 컴파일 옵션중에 /DYNAMICBASE /NXCOMPACT 라는 보안을 위한 컴파일 옵션이다. /DYNAMICBASE ASLR(Address Space Layout Randomization) 기능을 추가해 주는 컴파일 옵션인데재부팅시 마다 실행파일 인스턴스가 생성될 때 DLL같은 라이브러리의 적재위치실행코드 위치 라든지스택과 힙의 시작 위치를 랜덤화 시켜주는 기능을 한다오피스 2007을 컴파일 할때, ASLR을 지원하는 컴파일 옵션이 부여된 채 컴파일이 되었다.

/NXCOMPACT는 DEP(Data Execution Prevention) 기능을 추가해 주는 컴파일 옵션인데, XP sp2 Windows2003컴파일 할 때 적용이 되었다.

코드의 수행은 메모리에 바이트 뭉치를 가져와서 op code를 본 다음 indirect인지 direct인지 보고 실행하는 단순한 구조를 가지고 있다컴파일을 하게 되면 코드영역데이터 영역등으로 나누어 지는데여기서 문제는 잘못된 주소로 접근했을 때 그 바이트 뭉치가 데이터인지코드인지 PC가 가리키고 있는 주소 범위 말고는 판단할 수 없다는 것이다데이터 영역에 문제가 있는 코드를 넣어 놓고, PC를 그곳 주소로 변경만 시키면 해커들이 문제 있는 코드를 실행시킬 수 있다. DEP는 메모리 페이지가 스택이건 힙이건 간에 쓰기 상태가 가능하면 그 페이지의 코드 실행을 막아주는 기술이다.

UAC 지원을 위해서 Manifest UI상에서 제어할 수 있고 자동으로 만들어 주기 대문에 더 이상 UAC관련 Manifest를 손으로 작성하지 않아도 된다. Manifest를 손으로 작성할 때 assembly 부분을 잘못 작성하면 XP sp2에서는 잘 돌아가지만 XP에서는Manifest를 파싱할 때 exception이 나서 프로그램이 실행되지 않는 문제가 발생하기도 하고, trustInfo부분이 잘못되면 XP sp2라도 실행에 문제를 겪게 되는데인터넷상에는 의외로 문제 있는 Manifest가 떠돌아 다니는 경우가 많고프로그래머들이 이러한Manifest를 카피해서 프로젝트에 적용하는 케이스가 많게 된다.

 

2.     유니코드 프로그래밍

이번에 파라미터로 멀티바이트를 넘겨주던 일부 MFC 메소드 들이 모조리 Unicode로 변경이 되었다. ( 참고 :http://blog.naver.com/drvoss/20044451698 ) 뿐만아이라 이번에 추가가된 MFC9.0 Office, VS, IE UI의 드로잉코드가 들어 있는Visual Manager의 경우 유니코드만 지원하게 된다. Visual Manager 자체는 멀티바이트를 써도 상관없지만래핑하고 있는Control들의 코드가 모두 Unicode만 지원하기 때문에 새로운 MFC의 경우 Unicode만 지원한다고 해도 되겠다.

Unicode만 지원하는게 불편할 수도 있겠지만장점도 많이 있다멀티바이트를 혼용해서 쓰는 부분에는 항상 실수를 할 수 있는 여지가 있기 때문이다.

 

3.     보다 뛰어난 인텔리센스

인텔리 센스 부분이 많이 변경이 되었는데, VC NCB파일에 코드 파싱정보를 기술해 놓는다작은 DB파일이라고 봐도 무방한데, VS IDE는 아주 많은 스레드를 가지고 있는 멀티 스레드 애플리케이션이다인텔리센스 관련해서도 항상 많은 스레드가 돌고 있게 되는데인텔리센스용 멀티스레드에서 이 NCB파일을 접근하므로 NCB파일에 접근할 때는 간단한 locking 방법이 적용이 되어 있다그런데 이 locking 방법이 매우 비효율적으로 되어 있는 부분을 수정했고, NCB 파일을 제너레이트 하는 부분을 따로 스레드로 떼어 내고그밖에 병목현상이 있을 만한 부분을 모두 스레드로 떼어 냈다많은 기능들이 한 스레드에 있었던 2005에서는 인텔리센스 때문에 코드 에디팅이 멈칫 하는 현상이 발생되기도 했는데, 2008에서는 이제 멈칫 하는 현상은 절대 없을꺼라 하고소스 코드가 크더라도 파싱하는데 전혀 방해를 받지 않으면서인텔리센스가 100% cpu를 먹는 일은 절대 없도록 변경되었다.

 

4.     TR1의 지원

이번에 BOOST의 일부 기능들이 들어있는 TR1 SCL에 추가 되면서 Dinkumware에서 개발한 라이브러리를 VC에 가져 왔다다른 회사 라이브러리 가져온 건데기존 타회사에서 구현된 라이브러리와 차이점이 뭐냐 라고 할 수 있는데, 2005에서 STL에 적용되었던 것과 비슷하다고 보면 된다기존에 VC에 포함되어 있는 라이브러리들과 혼용해서 사용했을 때 최대 효과가 날 수 있도록 수정하고 /clr, /clr:pure, /W4, /Za, /Gz, /analyze 옵션에서 워닝등의 동작이 나지 않도록 수정했으며 무엇보다도 IDE Debugger Visualizer에 착 달라 붙는다. 2005 STL Debugging에서 컨테이너의 항목들이 원하던 대로 나오는 것에 감동했다면 2008 TR1에서 다시 한번 그 감동을 느낄 수 있을 것이다.

 

5.     IDE 확장

Class Designer가 지원이 되는데인수인계 문서를 만들거나 레포트 장수 늘릴 때 아주 좋다간단하게 지원이 되지만클래스 관계라 맴버함수/변수들이 잘 나오고오피스나 파워포인트에 자유 자재로 복사/붙여 넣기 할 수 있다.

 

 

TP 경로 참고 : http://blog.naver.com/drvoss/20048401998

 

인텔리센스가 나오고 있을 때 컨트롤을 누르게 되면 인텔리센스창이 흐릿해 지는 효과가 들어갔다인텔리 센스 창이 나오면esc키로 얼른 닫아 버리는 나에게 좋은 기능이 될 수 있을지는 모르겠다.

 

6.     더 훌륭해진 컴파일러

/MP라는 컴파일옵션이 추가 되었는데병렬 빌드를 할 수 있는 옵션이다. 2005에서도 병렬 프로젝트 빌드 하는 옵션이 있었는데, Tools > Options > Project and Solutions > Build and Run > Parallel project builds에 숫자가 병렬 빌드 되는 프로젝트의 개수를 설정할 수 있는 옵션이였다하나의 솔루션에 두개 이상의 프로제트를 만들고 빌드를 하게 되면 빌드 output 창에 아래와 같이1 2가 교대로 나오면서 빌드되는 모습을 볼 수 있다.

 

1>------ Rebuild All started: Project: p1, Configuration: Debug Win32 ------

2>------ Rebuild All started: Project: p2, Configuration: Debug Win32 ------

2>Deleting intermediate and output files for project 'p1', configuration 'Debug|Win32'

1>Deleting intermediate and output files for project 'p2', configuration 'Debug|Win32'

1>Compiling...

2>Compiling...

1>stdafx.cpp

2>stdafx.cpp

 

이 숫자가 의미 하는게 빌드 되는 프로젝트의 순서를 의미 하는데 1 2가 번갈아 나오면서 같이 병렬 빌드 되는 것을 확인할 수 있다그런데 이 병렬프로젝트 빌드는 별로 유용하지 않는 케이스가 많은데서로 디펜던시가 걸려 있는 경우 병렬 빌드가 되지 않을 뿐더러하나의 솔루션에 일부 프로젝트에만 병렬 빌드가 되고 나머지는 순차 빌드가 되어 버리면 병렬빌드의 부하 때문에 별로 이득을 보지 못하는 경우가 많다.

/MP 옵션은 프로젝트 별로 병렬 빌드를 할 수 있게 하고병렬빌드시 사용할 프로세서(프로세스)의 개수를 선택할 수 있다예를 들어 빌드 머신에 4개의 CPU가 달려 있고, /MP3 를 했다면 3개의 CPU를 빌드에 사용하고빌드를 시작 하면 3개의 빌드용 프로세스가 떠서 하나의 Unit(cpp 같이..)을 가져가면서 나누어 각각 빌드가 되게 된다.

병렬 빌드 옵션과 병렬 프로젝트 빌드 옵션을 자신의 빌드 머신의 프로세스 개수에 맞추어 놓고 인크리디빌드를 사용하면 화룡점정일 것이다.

참고로 VC10에서는 MSBuild를 이용해서 분산 빌드 시스템을 만들어 주는 기능이 추가될 계획이라고 한다.

 

7.     더 괜찮아진 IDE

비스타가 발표 되면서 MS에서는 Vista Programming Guideline을 발표 했는데, UAC를 고려한 어플리케이션 정책을 설계 하고, UAC는 한번만 뜨게 하며대부분의 경우 User Mode에서 수행하게 하고 필요할 때만 관리자권한으로 실행 되게끔 해라라고 했는데 정작 비스타 위의 2005는 전혀 가이드라인과 맞지 않았다.

2005에서는 이런 Vista Programming guideline 이 잘 적용이 되었는데, User 권한에서도 잘 실행이 되고관리자권한의 build후 디버깅을 할 때 별도의 관리자 권한 VS 인스턴스를 띄울 것인가란 다이얼로그를 내어 준다.

 

8.     코드 퍼포먼스 향상

새로운 CPU Architecture들의 Instruction들을 지원을 해준다특히 Intel Core Architecture에 대한 지원이 강화되어서재컴파일 하는 것 만으로도 요즘 일반화 되어 있는 Core Architecture 환경에서 2005에서 컴파일 한것보다 퍼포먼스가 좋게 나오게 할 수 있다. __cpuid 함수가 업그레이드 되었는데최신 프로세서를 지원할 분 아니라 로직적인 부분도 개선이 되었다고 한다.

그밖에 멀티 스레드 환경의 디버깅을 할 수 있는 다양한 기능들이 추가 되었다.

 

9.     MFC/ATL의 변화

Office, VS, IE를 지원하는 새로운 UI들과 라인별로 색깔 들어가는 리스트컨트롤일반 버튼에 속성 하나 더 주고 만들 수 있는 스플릿 버튼링크 컨트롤, Shell Tree, 그밖에 비스타에서 추가된 컨트롤들을 모두 지원하고 MFC에 추가 되었으며, 150개가 넘는 메소드와 수십개의 새로운 클래스가 추가 되었다그리고 ATL Server CodePlex로 이관 릴리즈 되어서 atl용 유틸리티나 인코딩,디코딩하는 여러 함수들을 사용할 수 없게 되었고정식 지원해 주지 않고 설치도 되지 않는다.

 

 

아, 자고 일어나서 바로 장문의 글은 힘들구나.. 다시 위로 올라가서 읽어 보기 싫다.ㅠ_ㅠ

Posted by 모과이IT
,

가 있어서 올려봅니다.

 

1.       Windows 단축키 Alt + ESC

모두들 alt + tab 을 사용하는데 Alt + ESC 키도 창전환을 하는 키 입니다. Alt + tab 과의 차이점은 몇몇 프로그램에서 속성창을 띄웠을 때 창 전환이 안되는데 Alt + ESC 는 창 전환이 됩니다그리고 아이콘 썸네일이 안나와서 빠르게 전환 됩니다.

WindowsXP PowerToy에 있는 http://www.ntwind.com/software/taskswitchxp.html 요걸 쓰면 Alt + tab 을 눌렀을 때 어떤 창인지 썸네일을 보여주는데 창 전환이 느린 단점이 있습니다빠르게 다음창으로 전환할 때 Alt + ESC 를 씁니다.

TaskSwitchXP Start Panel style

 

 

2.       Visual Assist 단축키 shift + ctrl + v

 함수에서 조금, B 함수에서 조금, C 함수에서 조금의 코딩을 함수로 복사 할 때, A 함수를 찾아 들어가 복사하고, D 함수에 붙여넣기, B 함수를 찾아 들어가 복사하고 함수에 붙여 넣기 이 동작을 반복하다 보면내가 조금전에 무엇을 복사하고무엇을 붙여 넣기 했는지 아노미 상태에 빠지는 경우가 있습니다. Shift + ctrl + v 는 복사했던 히스토리를 보여줍니다물론 클릭하면 히스토리에 복사되었던 내용을 붙여 넣기 합니다.



Posted by 모과이IT
,
클립보드

클립보드는 특정 프로그램에서 데이터를 다른 프로그램으로 넘어갈수 있도록 해주는 윈도우의 기능입니다. 보통 “복사하기” 나 "Copy"항목을 이용하면 현재 설정된 데이터가 클립보드로 넘어갑니다. 클립보드로 넘어간 데이터를 다른 프로그램에서 "붙여넣기“ 나 "Paste”를 이용하면 클립보드에 있는 데이터를 넘겨 받을수 있습니다. 클립보드에는 문자,그림 등의 형태를 저장할수 있으며 OLE를 이용하게 되면 다양한 형태의 데이터를 클립보드로 저장하고 저장된 데이터를 클립보드에서 얻을수 있습니다. 본항목에서는 클립보드에 데이터를 넣는 방법과 데이터를 읽는 방법에 대해서 설명합니다.


클립보드 상태 알기

클립보드를 열고자 할경우에는 OpenClipboard와 CloseClipboard 함수를 사용하면 됩니다.

OpenClipboard();//클립보드를 연다.

  :  클립보드에 데이터를 넣던지 또는 데이터를 얻는다.

CloaseClipboard()//클립보드를 닫는다.

클립보드에는 다양한 포맷형태로 데이터가 저장되어 있습니다. 이런 클립보드에서 현재 원하는 형식의 데이터가 있는지를 확인할 때 사용하는 함수가IsClipboardFormatAvailable  입니다.


BOOL IsClipboardFormatAvailable(

UINT format //클립보드 데이터 포맷

); 

format는 다음과 같은 값을 가집니다.


CF_BITMAP : HBITMAP 형태의 데이터

CF_DIB : 장치독립적인 비트맵 데이터 보통 bmp파일에서 로드된 데이터

CF_TEXT : 문자 데이터

CF_PALETTE  : 팔레트 데이터

CF_RIFF : 멀티미디어에서 사용하는 오디오 음성등의 데이터

CF_WAVE : 웨이브 파일 포맷

CF_TIFF : 태그 이미지 파일 포맷


위의 플러그들외에서 다양한 플러그들이 있는데 그만큼 클립보드에 저장할 데이터는 광범위하다는 의미입니다. 예를 들어 현재 복사할 텍스트가 있는가를 확인하고자 한다면

 BOOL check;

 check=IsClipboardFormatAvailable(CF_TEXT);

하시면 됩니다.

만일 CF_TEXT 형의 데이터가 클립보드에 있다면 check는 TRUE값이 그렇지 않다면 FALSE값이 설정됩니다.

실제 클립보드에서 텍스트를 얻고자 한다면 다음과 같은 방법을 취할수 있습니다.


OpenClipboard();//클립보드를 연다.

if(IsClipboardFormatAvailable(CF_TEXT))//클립보드에 텍스트 데이터가 있으면

{

    클립보드에서 데이터를 얻는다..

}

CloaseClipboard()//클립보드를 닫는다.


때로는 클립보드에 있는 데이터를 삭제하고자 할경우가 있습니다. 이때 사용하는 함수가 EmptyClipboard입니다.

만일 어떤 데이터를 클립보드에 넣고자 한다면 그리고 현재 있는 데이터를 삭제하고자 한다면 다음과 같이 할수 있습니다.


OpenClipboard();//클립보드를 연다.

EmptyClipboard();//클립보드에서 데이터를 지운다.

CloaseClipboard()//클립보드를 닫는다.


클립보드에서 데이터 얻기와 데이터 쓰기

클립보드에서 데이터를 얻고자 할 경우 GetClipboardData 함수를 사용합니다.

예를 들어서 클립보드에서 텍스트 데이터를 얻고자 한다면 다음과 같이 할수 있습니다.

         HANDLE hClipboard;

hClipboard=GetClipboardData(CF_TEXT);

만약 텍스트 데이터가 없다면 hClipboard에는 NULL값이 전송될것입니다. 넘겨주는 형식이 메모리 핸들러 형식이기 때문에 이데이터를 사용허가권을 받기 위해서는 GlobalLock를 사용해야 합니다.


 HANDLE hClipboard;

 LPASTR pClipboard;

hClipboard=GetClipboardData(CF_TEXT);

pClipboard=GlobalLock(hClipboard);

 //pClipboar를 사용한다.


위와 같이 하면 클립보드에 있는 데이터를 바로 사용하는 형식이 됩니다. 때로는 클립보드에 있는 데이터를 자신의 프로그램 내로 복사하고자 할경우가 있습니다. 이럴경우에는 클립보드에 저장된 데이터 크기만큼 메모리를 확보한후에 데이터를 복사하면 됩니다. 다음은 클립보드에서 데이터를 복사하는 예입니다.



           //클립보드를 연다

        OpenClipboard(hwnd);

        //문자형식의 데이터를 클립보드에서 얻는다.

        hClipboard=GetClipboardData(CF_TEXT);

        //hClipboard가 NULL이면 클립보드를 닫는다.

        if(!hClipboard)

        {

                CloseClipboard();

                 return 0;

        }

        //클립보드 데이터를 넣을 메모리 할당

        szBuff=GlobalReAlloc(szBuff,GlobalSize(hClipboard),GMEM_MOVEABLE);

        //메모리 사용 허가권을 받는다.

        pBuff=(LPSTR)GlobalLock(szBuff);

        //클립보드 메모리 사용허가권을 받는다.

        pClipboard=GlobalLock(hClipboard);

        //클립보드 데이터를 복사한다.


클립 보드에 데이터를 넣고자 한다면 SetClipboardData를 사용하여 간단하게 넣을 수 있습니다.


HANDLE SetClipboardData(

UINT uFormat, // 클립 보드 데이터 포맷

HANDLE hMem // 데이터 적재 메모리 핸들

); 


위의 예에서 pBuff에 있는 데이터를 수정한후 다시 클립보드에 넘기고자 할 때 있대 pBuff를 넘겨주는 것이 아니라 szBuff를 넘겨주어야 합니다. 클립보드로 넘어가는 것은 메모리 사용 허가 포인터 변수가 아니라 메모리 핸들러 이기 때문입니다.

 SetClipboardData(CF_TEXT,szBuff); 

// ex)

void CAddPeerDlg::OnBnClickedCopyInfo()
{

 if ( !OpenClipboard() )
 {
  AfxMessageBox( "Cannot open the Clipboard" );
 }

 // Remove the current Clipboard contents  
 if( !EmptyClipboard() )
 {
  AfxMessageBox( "Cannot empty the Clipboard" );
 }

 LPTSTR  lptstrCopy; 
 HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, 100 * sizeof(char));

 lptstrCopy = (LPTSTR)GlobalLock(hglbCopy);

 UpdateData(TRUE);

 memcpy( lptstrCopy, m_cstrLocalIP.GetBuffer(), 15 );
 memcpy( lptstrCopy + 15, &m_uiLocalPORT, 2 );
 memcpy( lptstrCopy + 15 + 2, m_cstrExternalIP.GetBuffer(), 15 );
 memcpy( lptstrCopy + 15 + 2 + 15, &m_uiExternalPORT, 2 );
 lptstrCopy[99] = 0;

 GlobalUnlock(hglbCopy);

 // For the appropriate data formats...
 if ( ::SetClipboardData( CF_TEXT, lptstrCopy ) == NULL )  
 {
  AfxMessageBox( "Unable to set Clipboard data" );    
  CloseClipboard();
 }

 // ...  
 CloseClipboard();

}

 

 

 

void CAddPeerDlg::OnBnClickedPasteInfo()
{

 if ( !OpenClipboard() )
 {
  AfxMessageBox( "Cannot open the Clipboard" );
 }

 LPTSTR  lptstrCopy; 
 HGLOBAL hglbCopy = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(char)*100);     // 전역메모리할당
 hglbCopy = GetClipboardData( CF_TEXT );

 lptstrCopy = (LPTSTR)GlobalLock(hglbCopy);

 m_cstrLocalIP = "";
 m_uiLocalPORT = 0;
 m_cstrExternalIP = "";
 m_uiExternalPORT = 0;

 char cTemp[100];
 memcpy( cTemp, lptstrCopy, 15 );
 m_cstrLocalIP = cTemp;
 memcpy( &m_uiLocalPORT, lptstrCopy + 15, 2 );
 m_uiLocalPORT = m_uiLocalPORT&0xffff;

 memcpy( cTemp, lptstrCopy + 15 + 2, 15 );
 m_cstrExternalIP = cTemp;
 memcpy( &m_uiExternalPORT, lptstrCopy + 15 + 2 +15, 2 );
 m_uiExternalPORT = m_uiExternalPORT&0xffff;

 UpdateData(FALSE);

 GlobalUnlock(hglbCopy);

 // ...  
 CloseClipboard();

}

Posted by 모과이IT
,