This 포인터란?  
 : This Pointer란 객체가 멤버함수를 호출할 때 자동으로 멤버함수에게 전달되는 포인터이며, 호출한 객체를 가리키는 포인터를 말한다. 흔히 자기 참조 포인터(자기 자신을 가리킬 수 있는 포인터)라고 불리우며, 멤버 함수에게만 this pointer가 전달된다.(Friend 함수는 멤버 함수가 아니므로 this pointer가 전달되지 않는다. Static 함수도 마찬가지이다.) 아래의 예제를 보면 금방 감이 올것이다. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person    
  5. {   
  6. public:   
  7.     Person* GetThis(){         
  8.         return this//this 포인터를 리턴.   
  9.     }   
  10. };   
  11.   
  12. int main()   
  13. {   
  14.     Person *p1 = new Person();   
  15.     cout<<"포인터 p1: "<<p1<<endl;   
  16.     cout<<"p1의 this: "<<p1->GetThis()<<endl<<endl;   
  17.   
  18.     Person *p2 = new Person();   
  19.     cout<<"포인터 p2: "<<p2<<endl;   
  20.     cout<<"p2의 this: "<<p2->GetThis()<<endl;   
  21.   
  22.     return 0;   
  23. }  

 
  결과를 보면 알 수 있듯이, This 포인터는 멤버 함수를 소유한 객체를 가리키고 있다. 다시 말해서 멤버 함수를 호출할 때 사용한 객체를 가리키고 있다. 그럼 이런 this pointer의 타입은 어떻게 되는것일까? 일단 객체를 가리키는 것이므로 Person 클래스의 포인터 타입이 여기서 this의 리턴 타입이라고 할 수 있다. (Person * 타입)

Friend  
 :  일반적으로 클래스의 비공개멤버(private)는 외부함수에서 접근할 수 없다. 해 갈 수 없다. private 변수, 함수는 멤버 함수에서만 접근이 가능하기 때문이다. 그런데 멤버함수가 아닌 외부함수에서 접근해야 할 경우가 발생될 수 있다. 그래서 나타난게 friend 라는 문법이다. 그런 경우 클래스에서 프렌드함수로 선언해 주면 외부함수이면서 클래스의 비공개 멤버에 접근할수 있는 권한을 갖게 된다.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Counter   
  5. {   
  6. private:   
  7.     int val;   
  8.   
  9. public:   
  10.     Counter() {    
  11.         val=0;   
  12.     }   
  13.     void Print() const {   
  14.         cout<<val<<endl;   
  15.     }   
  16.            
  17.     friend void SetX(Counter& c, int val);  //friend 선언.   
  18. };   
  19.   
  20. void SetX(Counter& c, int val) // 전역함수.   
  21. {   
  22.     c.val=val;   
  23. }   
  24.   
  25. int main()   
  26. {   
  27.     Counter cnt;   
  28.     cnt.Print();   
  29.   
  30.     SetX(cnt, 2002);   
  31.     cnt.Print();   
  32.   
  33.     return 0;   
  34. }  
  1.   
  2. #include <iostream>   
  3. using std::cout;   
  4. using std::endl;   
  5.   
  6. class AAA   
  7. {   
  8. private:   
  9.     int data;   
  10.     friend class BBB;  // class BBB를 friend로 선언함!   
  11. };   
  12.   
  13. class BBB   
  14. {   
  15. public:   
  16.     void SetData(AAA& aaa, int val){   
  17.         aaa.data=val; //class AAA의 private 영역 접근!   
  18.     }   
  19. };   
  20.   
  21. int main()   
  22. {   
  23.     AAA aaa;   
  24.     BBB bbb;   
  25.   
  26.     bbb.SetData(aaa, 10);   
  27.   
  28.     return 0;   
  29. }  

 위 두 예제를 보면 쓰는 용법에 대해서는 간단히 한눈에 알아 볼 수 있다. friend는 함수, 클래스에 적용해서 쓸 수 있다. friend가 가지는 특징을 살펴 보면 아래 4가지 정도로 요약 할 수 있다. 
 1. 클래스에 friend 키워드와 함께 원형을 명시 해야 한다. 
 2. 상속 되지 않는다. 
 3. friend 함수는 클래스의 멤버가 아니라 일반함수이다. (멤버 접근 연산자로 접근하지 않는다.)
 4. 보통 사용되는 곳은 연산자 중복 사용시, 하나의 함수에서 다른 클래스들의 비공개 멤버로 접근시 사용된다. (전방참조)

※ 전방참조(Forward Reference)  : 두개 이상의 클래스를 동시에 선언하는 것은 논리적으로 불가능하므로 컴파일러에게 미리 이 클래스가 있음을 알려주는 것

 보통 friend가 OOP(Object Oriented Programming)의 캡슐화와 정보은닉을 해친다는 이야기가 있지만, 대다수의 경우 friend는 멤버의 개수를 줄여줌으로서 캡슐화에 더 이로운 영향을 끼친다. (public 멤버가 적을수록 캡슐화가 좋아진다)  friend를 피하기 위해 인위적으로 public 멤버를 추가한다면 그것이 오히여 OOP에 악역향을 끼친다. 
Posted by 모과이IT
,
저번 포스팅에 이어 두번째로 이번에는 소멸자에 대해서 얘기해 보겠습니다. 우선 소멸자를 이야기 하기 전에 객체가 소멸되는 시점에 관해서 언급 해보도록 하죠. 

* 객체가 소멸되는 시점 : 함수 내에 지역적으로 변수가 선언되면 함수 호출이 끝남과 동시에 소멸이 된다. 이와 마찬가지로 객체도 함수내에서 선언된다고 하면, 함수 호출이 끝나면 소멸되게 된다. 

* 전역적으로 선언된 객체 
: 전역변수는 프로그램 시작과 동시에 메모리에 올라갔다가, 프로그램이 종료될때 소멸된다. 객체도 마찬가지로 똑같은 동작을 하지만, 객체는 이렇게 생성할 일은 거의 없다고 보면 된다. 

* 전역 : 일반적인 객체지향에 전역이란 개념은 존재 하지 않는다. 이를 대신하기 위한 static 멤버 변수, 멤버 함수가 존재 한다. (이거 대해서는 나중에...)

소멸자 (Destructor)  
 : 객체의 메모리 반환을 위해서 객체 소멸시 자동 호출되는 함수로 클래스의 이름 앞에는 ~가 붙은 형태를 띄우며, 리턴하지 않고, 리턴 타입도 없다. 전달인자는 항상 Void 형으로 오버로딩, 디폴트 매개변수의 선언이 불가능하다는 특징을 가지고 있다. 
  그럼 아래의 동적 할당 예제를 통해서 소멸자가 어떻게 동작하고 있는지 한번 자세히 알아 보도록 하자. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class DynamicArray{   
  5. public:   
  6.     int *arr;   
  7.   
  8.     DynamicArray(int arraySize)   
  9.     {   
  10.         arr = new int [arraySize]; //인자로 받는 크기만큼 메모리를 할당한다.   
  11.     }   
  12.        
  13.     //소멸자, 메모리를 해제한다.    
  14.     ~DynamicArray()   
  15.     {   
  16.         delete[] arr;   
  17.         arr = NULL;   
  18.     }   
  19. };   
  20.   
  21. void main()   
  22. {   
  23.     int size;   
  24.     int i;   
  25.     cout<< "몇개의 정수를 입력하겠는가? ";   
  26.     cin>>size;   
  27.   
  28.     DynamicArray da(size);   
  29.   
  30.     cout<< size <<"개의 정수를 입력 하시오. "<<endl;   
  31.     for(i=0; i< size; i++)   
  32.         cin>>da.arr[i];   
  33.   
  34.     for(i = size -1 ; i>=0; --i)   
  35.         cout<< da.arr[i]  << " ";   
  36.   
  37.     cout<<endl;      
  38. }  
- 결과값 

  DynamicArray 클래스는 내부적으로 동적 메모리 할당을 사용한다. 28번째 줄에서 DynamicArray 타입의 객체 da를 생성하면서 필요한 메모리의 크기를 인자로 전달하는데, 10번째 줄처럼 메모리를 동적으로 할당하고 그 주소를 arr 멤버 변수에 보관해준다. 아마 DynamicArray를 생성하면 아래와 같은 모습을 띄고 있을 것이다. 

 결국 main() 함수가 끝나면 da 객체가 자동적으로 소멸하게 된다. 함수 안에서 정의한 변수는 함수가 종료와 동시에 소멸되므로, 객체가 소멸하면서 자동적으로 소멸자가 호출되고, 16번째 줄처럼 소멸자 안에서 arr이 가리키는 메모리를 해제 한다. 
  만약 소멸자가 없었다 라고 한다면, 우리는 클래스를 만들고 프로그램을 짤때마다 일일히 다 해제를 해줘야 한다. 만약 프로그램의 크기가 커진다고 생각하면, 참 큰일이 아닐수 없다. 

객체의 소멸 순서  
 : 객체의 소멸 순서는 소멸자 호출 -> 메모리 반환 순서로 객체가 소멸되고, 소멸자의 호출을 먼저 해주는 이유는 소멸자의 호출을 먼저 해줌으로서, 메모리가 반환되어질때, 반환되어지지 않은 메모리 공간을 명시적으로 반환해주기 위해서이다. 

 기본적으로 소멸자를 명시해주지 않아도 디폴트 소멸자가 사용되고, 디폴트 소멸자는 디폴트 생성자와 같은 특징을 가지고 있다. 소멸자의 명시적 제공은 첫번째, 생성자에서 메모리를 동적으로 할당하는 경우나 디버깅시 사용자가 객체의 소멸되는 시점을 알고 싶을때 사용을 한다. 
Posted by 모과이IT
,
1. 생성자 (Constructor)   
 :  생성자는 객체를 생성할 때 자동적으로 호출되는 함수로 클래스와 같은 이름의 형태를 띤 함수이며, 리턴형이 없으며, 리턴 하지도 않는 특징을 가지고 있다. 그럼 왜 생성자가 필요할까? 바로 객체를 생성과 동시에 초기화를 해주기 위해 필요하다.  
  객체를 생성과 동시에 초기화 주는 것이 클래스의 좋은 구조이기 때문에, C++는 정보은닉과 더불어 생성과 동시에 초기화라는 안전성을 제공하는 생성자라는 문법을 제공해 주는 것이다. 

1-1. 생성자의 사용?  
 : 생성자의 형태를 보면, 일단 세가지 형태를 가지고 있다. 첫번째로 디폴트 생성자 (Default Constructor), 인자가 있는 생성자, 마지막으로는 복사 생성자 (Copy Constructor) 이 세가지 형태가 있다. 

- 디폴트 생성자  
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person{   
  5. public:   
  6.     int number;   
  7.     char *name;   
  8.        
  9.     //생성자   
  10.     Person()   
  11.     {   
  12.         number =0; name = "Noname";    
  13.     }      
  14.     void print()   
  15.     {   
  16.         cout<< "Number : "<<number <<" Name :" <<name <<endl;   
  17.     }   
  18. };   
  19.   
  20. int main()   
  21. {   
  22.     Person P;   
  23.     P.print();   
  24.   
  25.     return 0;   
  26. }  
 위에서 생성자 부분을 보면, 생성자의 원형만 적어 주었다. 생성자는 클래스와 동일한 이름을 가진 멤버 함수로, 다른 멤버 함수와 같이 클래스 안이나 바깥쪽에서 정의하는 것이 가능하다.  객체를 생성하면 기본 적으로 생성자가 호출 되는데, 이렇게 인자가 없는 생성자를 디폴트 생성자라고 부른다. 
  굳이 생성자를 저렇게 명시적으로 적어 주지 않더라도 클래스에서는 디폴트 생성자를 호출한다. (하지만 초기화에 대한 기대는 버려야 할 것이다.)

- 인자가 있는 생성자
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person{   
  5. public:   
  6.     int number;   
  7.     char *name;   
  8.        
  9.     //인자가 있는 생성자   
  10.     Person(int _number, char* _name)   
  11.     {   
  12.         number =_number; name = _name;   
  13.     }      
  14.     void print()   
  15.     {   
  16.         cout<< "Number : "<<number << endl<<"Name :" <<name <<endl;   
  17.     }   
  18. };   
  19.   
  20. int main()   
  21. {   
  22.     Person P(1, "myname");   
  23.     P.print();   
  24.   
  25.     return 0;   
  26. }  
 인자가 있는 생성자는 별도로 정의되어 있는 용어는 아니지만, 말그대로 생성자에 인자값을 가진것이 바로 인자가 있는 생성자이다. 디폴트 생성자와 크게 다를바가 없지만, 인자가 있기 때문에 어떻게 인자를 넘겨줄 것인지가 문제가 된다.. 
  생성자는 객체를 생성할 때, 호출되기 때문에 인자를 넘겨주는 것도 객체를 생성할때 위와 보는 것과 같이 main 함수에서 해주어야 한다.  이렇게 인자가 있는 생성자를 사용하면 객체의 생성과 동시에 초기화 하는 것이 편해진다. 

- 복사 생성자
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. class Person{   
  5. private:   
  6.     int number;   
  7.     char *name;   
  8. public:   
  9.        
  10.     Person()   
  11.     {   
  12.         number = 0; name = "noname";   
  13.     }   
  14.     Person(int _number, char* _name)   
  15.     {   
  16.         number =_number; name = _name;   
  17.     }   
  18.        
  19.     //복사 생성자   
  20.     Person(const Person& p)   
  21.     {   
  22.         cout<<"Copy Constructor Call"<<endl<<endl;   
  23.         number = p.number;   
  24.         name = p.name;   
  25.     }   
  26.     void print()   
  27.     {   
  28.         cout<< "Number : "<<number << endl<<"Name :" <<name <<endl;   
  29.     }   
  30. };   
  31.   
  32. int main()   
  33. {   
  34.     //객체 생성   
  35.     Person P1(1, "myname"), P2(2, "your name");   
  36.     Person P3 = P1;   
  37.   
  38.     P3.print();   
  39.     P3 = P2;   
  40.        
  41.     cout<<endl;   
  42.     P3.print();   
  43.   
  44.     return 0;   
  45. }  
 복사 생성자는 다른 객체로부터 값을 복사해서 초기화하는데 사용하며, 자신과 동일한 타입의 객체에 대한 레퍼런스를 인자로 받는 생성자이다. 인자값에 Const를 써준 이유에 대해서는 아래의 링크를 참조 하면 도움이 될 것이다. 


  복사 생성자는 36번째 줄처럼 객체를 사용해서 초기화하는 경우에 호출이 된다 .디폴트 생성자를 호출하고 나서 복사 생성자를 또 호출하는게 아니라, 오직 복사 생성자만 호출한다. 
  우리가 복사 생성자를 만들지 않아도, 36번째 줄처럼 초기화는 잘 작동을 한다. 그럼 왜 복사 생성자를 만들어야 하나? 그 이유는 1:1 복사 하는 것 말고 다른 방식으로 복사하고 싶은 경우가 있기 때문에 그런 경우에 복사 생성자를 재정의 해서 사용하면 될것이다. 

쓰다 보니 길어져서, 소멸자에 대해서는 다음 포스팅에서 이야기 해 보도록 하겠습니다.
Posted by 모과이IT
,
1. 정보은닉 (Information Hiding)  

 : 프로그램을을 사용하는 사용자가 알아야 하는 것은 프로그램 사용법이지 프로그램의 내부 동작이나 상세 구조가 아니다. 사용자가 굳이 알 필요가 없는 불필요한 정보는 숨김으로써 사용자는 최소한의 정보만으로 프로그램을 쉽게 사용할 수 있어야 한다. 

  C++에서는 클래스의 정보 은폐 기능을 지원하기 위해 private, public, protected 등의 접근제어 키워드를 통해 선언된 클래스 외부에서 직접적인 접근을 허용하지 않는것을 정보은닉이라 할 수 있다. 
  하지만 간접적 접근 경로를 제공해줘야 한다. 숨길 멤버와 공개할 멤버의 블록을 구성하도록 해서, 공개된 멤버는 외부에서 자유롭게 읽을 수 있지만 숨겨진 멤버를 참조하려고 시도하면 컴파일 과정에서 접근할 수 없다는 에러로 처리를 하면 된다.  (아래는 정보은닉의 예)
  1. #include<iostream>   
  2. using namespace std;   
  3.   
  4. class Point   
  5. {   
  6.     int x;   // x좌표의 범위 : 0 ~ 100   
  7.     int y;   // y좌표의 점위 : 0 ~ 100   
  8. public:   
  9.     int GetX(){ return x; }   
  10.     int GetY(){ return y; }   
  11.   
  12.     void SetX(int _x){ x=_x; }   
  13.     void SetY(int _y){ y=_y; }   
  14. };   
  15.   
  16. int main()   
  17. {   
  18.     int x, y;   
  19.     cout<<"좌표입력 : ";   
  20.     cin>>x>>y;   
  21.   
  22.     Point p;   
  23.     p.SetX(x);   
  24.     p.SetY(y);   
  25.   
  26.     cout<<"입력 된 데이터를 이용해서 그림을 그림"<<endl;   
  27.     return 0;   
  28. }  
2. 정보 은닉의 필요성  

 : 캡슐화된 코드를 제공할때 함부로 건드려서는 안되는 코드들을 보호하기 위해, 프로그램의 안정적 구현을 위해서는 정보은닉이 필요하다.


3. 캡슐화 (Encapsulation)  

  : 캡슐화란 클래스를 정의하는데 있어서 관련있는 데이터 및 함수를 하나로 묶는것이다. 캡슐화를 함으로써 정보 은닉도 함께 가져오는 효과를 가져오기도 한다. 아래의 캡슐화가 된 예제와 캡슐화가 안된 예제를 한번 보시죠. 


3-1)  캡슐화가 안된 예제
  1. #include<iostream>   
  2. using namespace std;   
  3.   
  4. class Point   
  5. {   
  6.     int x;   // x좌표의 범위 : 0 ~ 100   
  7.     int y;   // y좌표의 범위 : 0 ~ 100   
  8. public:   
  9.     int GetX(){ return x; }   
  10.     int GetY(){ return y; }   
  11.   
  12.     void SetX(int _x);   
  13.     void SetY(int _y);   
  14. };   
  15.   
  16. void Point::SetX(int _x)   
  17. {   
  18.     if(_x<0 || _x>100) {   
  19.         cout<<"X좌표 입력 오류, 확인 요망"<<endl;   
  20.         return;   
  21.     }   
  22.     x=_x;   
  23. }   
  24. void Point::SetY(int _y)   
  25. {   
  26.     if(_y<0 || _y>100)   
  27.     {   
  28.         cout<<"Y좌표 입력 오류, 확인 요망"<<endl;   
  29.         return;   
  30.     }   
  31.     y=_y;   
  32. }   
  33.   
  34. class PointShow   
  35. {   
  36. public:   
  37.     void ShowData(Point p)   
  38.     {   
  39.         cout<<"x좌표: "<<p.GetX()<<endl;   
  40.         cout<<"y좌표: "<<p.GetY()<<endl;   
  41.     }   
  42.   
  43. };   
  44.   
  45.   
  46. int main()   
  47. {   
  48.     int x, y;   
  49.     cout<<"좌표입력 : ";   
  50.     cin>>x>>y;   
  51.   
  52.     Point p;   
  53.     p.SetX(x);   
  54.     p.SetY(y);   
  55.   
  56.     PointShow show;   
  57.     show.ShowData(p);   
  58.   
  59.     return 0;   
  60. }  
  예를 들어 클래스를 정의하고 나서 프로젝트가 진행된 상황에서 문제를 발견, 포인트 클래스에다가 자기가 지니고 있는 변수 x,y에 대해 출력하는 기능을 넣어 줘야 되겠구나 생각을 하게 된다고 가정을 해봅시다. 하지만 이미 point라는 클래스는 이미 정해져 있고, 프로젝트가 이미 진행이 되어 있기 때문에, 기존의 클래스를 변경한는 것은 어려운 일 일것이다. 
  그래서 일반적으로 출력이란 기능이 없으므로, 이에 대한 기능을 클래스로 정의 하기에 이를 것입니다. 포인트 클래스의 객체를 인자로 받아서 리턴되는 데이터를 출력하는 형태로 클래스를 정의하는 pointshow 라는 클래스를 만들게 되는 것이죠. 
  메인함수에서 포인트 객체도 생성하고, 출력을 위해 show라는 객체도 생성하게 되고, 결과는 무리 없이 출력 되겠지만, 이것은 캡슐화가 무너졌다 라고 볼 수 있습니다.
  showdata라는 함수는 내가 정의한 포인트 함수의 x,y를 출력하기 위한 함수 이므로, 이것은 포인트에 대한 기능을 가진 것이라고 볼 수 있으므로  showdata라는 함수는 포인트 클래스 안에 존재 하는것이 맞는 것이죠. 

3-2) 캡슐화된 예제 :  위 상황에서 캡슐화가 제대로 됐다고 하면, 아래와 같은 예제가 나올 것입니다. 
  1. #include<iostream>   
  2. using namespace std;   
  3.   
  4. class Point   
  5. {   
  6.     int x;   // x좌표의 범위 : 0~100   
  7.     int y;   // y좌표의 범위 : 0~100   
  8. public:   
  9.     int GetX(){ return x; }   
  10.     int GetY(){ return y; }   
  11.   
  12.     void SetX(int _x);   
  13.     void SetY(int _y);   
  14.   
  15.     void ShowData();  //캡슐화를 위해 추가된 함수.   
  16. };   
  17.   
  18. void Point::SetX(int _x)   
  19. {   
  20.     if(_x<0 || _x>100) {   
  21.         cout<<"X좌표 입력 오류, 확인 요망"<<endl;   
  22.         return;   
  23.     }   
  24.     x=_x;   
  25. }   
  26. void Point::SetY(int _y)   
  27. {   
  28.     if(_y<0 || _y>100)   
  29.     {   
  30.         cout<<"Y좌표 입력 오류, 확인 요망"<<endl;   
  31.         return;   
  32.     }   
  33.     y=_y;   
  34. }   
  35.   
  36. void Point::ShowData()   
  37. {   
  38.     cout<<"x좌표: "<<x<<endl;   
  39.     cout<<"y좌표: "<<y<<endl;   
  40. }   
  41.   
  42. int main()   
  43. {   
  44.     int x, y;   
  45.     cout<<"좌표입력 : ";   
  46.     cin>>x>>y;   
  47.   
  48.     Point p;   
  49.     p.SetX(x);   
  50.     p.SetY(y);   
  51.     p.ShowData();   
  52.   
  53.     return 0;   
  54. }  

4. 캡슐화를 하는 이유?  

 : 코드의 재활용성을 높이고 에러발생을 최소화하며 다이나믹한 속성을 높이기 위해서 캡슐화가 필요 하다.

Posted by 모과이IT
,
1. 값에의한 호출  

  : 값에 의한 호출은 Call By Value라고 불리우며, 인자로 넘기는 값을 복사해서 새로운 함수에 넘겨주는 방식으로 값의 복사에 의한 함수 호출을 의미합니다. 

  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. void swap(int a, int b);   
  5.   
  6. int main()   
  7. {   
  8.     int val1 = 10;   
  9.     int val2 = 20;   
  10.     swap(val1, val2);   
  11.   
  12.     cout<<"val1 : "<<val1<<endl;   
  13.     cout<<"val2 : "<<val2<<endl;   
  14.   
  15.     return 0;   
  16. }   
  17.   
  18. void swap (int a, int b)   
  19. {   
  20.     int temp = a;   
  21.     a = b;   
  22.     b = temp;   
  23.   
  24.     cout<<"a : "<<a<<endl;   
  25.     cout<<"b : "<<b<<endl;   
  26. }  

 위의 예제는 Call by Value 예제와 결과값 입니다. Call by Value는 값을 복사해서 전달하기 때문에 원본의 값이 변경될 가능성이 없다는 특징을 가지고 있지만, 값을 넘겨 줄때마다 고비용 , 복사손실 문제가 발생하는 단점도 있습니다. 
 ※ Call by Value는 Pass by Value 라고 불리기도 합니다. 

2. 참조에 의한 호출   

  : 참조에 의한 호출은 Call by Reference라고 부르며, 주소 값을 인자로 전달하는 함수 호출을 말합니다. 아래 예제를 보시죠. (위와 거의 유사합니다.)

  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. void swap(int *a, int *b);   
  5.   
  6. int main()   
  7. {   
  8.     int val1 = 10;   
  9.     int val2 = 20;   
  10.   
  11.     cout<< "Before the swap function" <<endl;   
  12.     cout<<"val1 : "<<val1<<endl;   
  13.     cout<<"val2 : "<<val2<<endl;   
  14.   
  15.     swap(&val1, &val2);   
  16.   
  17.     cout<<endl<<"After Swap function " <<endl;   
  18.     cout<<"val1 : "<<val1<<endl;   
  19.     cout<<"val2 : "<<val2<<endl;   
  20.   
  21.     return 0;   
  22. }   
  23.   
  24. void swap (int *a, int *b)   
  25. {   
  26.     int temp = *a;   
  27.     *a = *b;   
  28.     *b = temp;   
  29. }  

  위의 예제에서 알 수 있듯이 Call by Reference는 복사손실과 고비용 문제에서는 벗어났지만, 원본의 값의 변경이 일어 날 수 있다는 중대한 문제점을 안고 있는걸 볼 수 있습니다. 

 Call by Value 나 Call by Reference 둘다 문제점을 가지고 있는데요, Effective C++에서는 이거에 대한 개선점을 언급한 바가 있습니다. 바로 항목 20 에서 인데 그에 대한 설명은 아래 포스팅을 참조 해 주십시오. 

Posted by 모과이IT
,
우선 레퍼런스를 알아 보기 전에 변수에 대해서 간단히 알아 봅시다. 
1. 변수 (Variable)  

   :  변수란 메모리 공간에 붙은 이름 이라고 할 수 있습니다. C에서는 하나의 메모리 공간에 하나의 이름만을 가지고 있었는데, C++에서는 하나의 메모리 공간에 둘이상의 이름을 붙여 줄 수 있죠. 그것이 바로 레퍼런스입니다. 


2. 레퍼런스  

 : 이름을 지니는 대상에 별명을 붙여주는 행위. 아래와 같이 사용합니다. 

  1. int main()   
  2. {   
  3.     int val =10;   
  4.     int *pVal = &val; //주소 값을 얻기 위해 & 연산자 사용의 예   
  5.     int &Val = val; //레퍼런스 선언을 위한 &연산자 사용   
  6.   
  7.     return 0;   
  8. }  

 '&' 연산자를 이용해 마음대로 변수에다가 별명을 붙여 줄 수 있는데요. 아래 예제를 보시죠.
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. int main()   
  5. {   
  6.     int val =10;   
  7.     int &AAA = val; //레퍼런스 선언을 위한 &연산자 사용   
  8.     AAA = 20;   
  9.   
  10.     cout<< "val value is  : "<<val <<endl;   
  11.     cout<< "AAA value is  : "<<AAA <<endl;   
  12.   
  13.     val = 40;   
  14.   
  15.     cout<< "val value is  : "<<val <<endl;   
  16.     cout<< "AAA value is  : "<<AAA <<endl;       
  17.   
  18.     return 0;   
  19. }  
 아래 예제를 실행한 결과 입니다. 

 레퍼런스를 이용한 변수의 값을 변경하여도 어차피 AAA나 val은 같은 메모리 공간을 가지고 있기 때문에 위와 같은 출력 결과를 출력하는 것을 알 수 있습니다. 
Posted by 모과이IT
,
우리들이 프로그램을 실행하면, 운영체제는 우리들이 실행한 프로그램을 위해 메모리 공간을 할당해줍니다. 그냥 할당해 주느냐? 그것이 아니라 용도별로 메모리 공간을 할당해 운영체제가 메모리 영역을 구분해 메모리 공간의 효율적 사용을 하게 만들어 줍니다. 

1. 메모리 영역의 종류  
* 데이터영역 : 전역 변수, Static변수를 위한 메모리 공간
 - 전역 변수 : 프로그램이 시작하자마자 메모리 공간에 올라가서 종료 될때까지 남아있는다.
 - Static 변수 : 프로그램 시작과 동시에 메모리 공간에 올라가서 종료될때까지 남아 있긴 한다. 
* 힙영역 : 런타임에 크기가 결정될수 있는 요소들을 올리기 위한 공간
* Stack : 컴파일 타임에 크기가 결정될 수 있는 요소들을 메모리 공간에 올리기 위한 영역

 각각의 영역에 대한 설명을 들었습니다. 아래 예제를 한번 보시죠. 
  1. #include <iostream>   
  2. usng namespace std;   
  3.   
  4. void function (int);   
  5.   
  6. int main()   
  7. {   
  8.     int size;   
  9.     cin>>size;   
  10.     function(size);   
  11.     return 0;    
  12.     //메인함수에서 요구되어 지는 메모리 공간의 크기는 컴파일 타임에 크기가 결정 가능하다.    
  13.         //이런 것을 올리기 위한 영역이 Stack이다.   
  14. }   
  15.   
  16. void function(int i)   
  17. {   
  18.     static int i = 10; //funtion이라는 함수가 처음 호출될때 i라는 변수가 초기화 된다.    
  19.     int array[i];    
  20. }  
  위의 예제는 에러를 일으키는데 위에서  function이라는 함수는 컴파일 타임에 요구되어 지는 메모리 크기를 계산하기는 불가능합니다. array라는 배열을 선언하기에 있어서 그 크기를 전달되는 인자값에 의해서 결정되어 지는데, 이 i 값은 고정이 아니라, 프로그램을 실행하는 사람에 의해서 값 변동이 심할 것입니다. 
  그러면 컴파일 되는 동안에 funtion이라는 함수내에서 요구되어지는 메모리 크기를 컴파일 타임에 결정할 수 없게 되고, 이 크기는 입력되는 size값이 결정되어야만 결정이 되죠. (이 사이즈는 누군가 프로그램을 실행하여야 결정되기 때문에 Array배열의 사이즈 크기는  런타임에 결정이 된다.)

 - 일반적으로 int array[i]; 이렇게 선언하면, 스택에 올리라는 의미가 된다. (이것은 스택에 못올린다.) 그래서 우리가 힙이라는 메모리 공간에 이런 배열을 선언하기 위해 malloc과 free라는 함수를 사용하게 되는 것이죠. 

2. Malloc & Free  
- Malloc : 힙에다가 메모리 공간을 할당하기 위한 함수 , free는 malloc으로 할당된 메모리 공간을 해제 하기 위한 함수이다. 
- free 함수가 필요한 이유 : 힙이라는 메모리 공간은 프로그래머가 관리 하는 메모리 공간이기 때문에 malloc을 쓰면 free로 꼭 해제를 해줘야 한다. 
Posted by 모과이IT
,
1. Const?  
 : Const는 변수를 상수화 하기 위해 사용하는데, 쓰이는 위치에 따라서 용도가 조금 달라진다. 

- 첫번째, const int* n 
 :  위와 같이 const가 자료형 뒤에 붙은 경우를 데이터 상수화 라고 하고 n이라는 포인터가 가리키는 대상을 상수화 하겠다는 의미가 된다. 
  1. #include <iostream>   
  2. using namespace std;   
  3.   
  4. const int*n;   
  5.   
  6. void main()   
  7. {   
  8.     int b =10;   
  9.     n = &b;   
  10.        
  11.     b = 20;   
  12.     cout<< b<<endl; //허용   
  13.   
  14.     *n = 30; //error (you cannot assign to a variable that is const)       
  15. }  
 위의 예제를 한번 보자 . n이라는 포인터가 가리키는 곳 B로 지정하고, B의 값은 변경하면 무리 없이 변경되는 것을 볼 수 있지만, *n =30; 이 부분에서는 직접 실행해 보면, 에러가 나는것을 알 수 있다. n이 지닌 값은 변경이 안된다는 것이다. 즉, 실제로 메모리 공간 자체가 상수화 된것이 아니고, n이라는 포인터를 이용해서 데이터 변경을 막겠다는 의미가 된다. (하지만 변수 b를 이용해 변경은 가능하다)

- 두번째, int* const n
 :  위와 같이 cosnt가 붙은 경우를 포인터 상수화 라고 한다. 예제를 통해 한번 알아 보자.
  1. #include <iostream>   
  2.   
  3. void main()   
  4. {   
  5.     int a =20;   
  6.     int b =10;   
  7.     intconst n = &b;   
  8.        
  9.     n = &a; //you cannot assign to a variable that is const    
  10. }  
 위에서 같이 선언한 n이라는 포인터가 b를 가르키고 있을 시에, 이런 상황에 변수 a를 또 가리키도록 하려는 것을 허용 하지 않겠다는 의미가 된다.

- 세번째, const int* const n;        :    데이터, 포인터 둘다 상수화 하겠다는 의미이다. 
Posted by 모과이IT
,
1. 매크로 함수 (Macro Function)  
 : 전처리기에 의해 처리 되면서, 매우 빠른 실행의 이점을 가지게 되는 함수이다.  아래와 같이 선언해 쓰고, 매크로 함수는 데이터형에 영향을 받지 않는 특징을 가지고 있다. 
  1. #define Square ((x) * (x))  

하지만 왜 C++에 와서 그대로 매크로 함수를 쓰지 않고 인라인 함수라는 것을 새로 만들어서 사용하는 것일까? 그 이유를 한번 알아 보자.

2. 인라인 함수 (Inline Function)  
 : C와의 매크로 함수와 같지만, 인라인 함수는 컴파일러단에서 처리되면서, 구현이 용이 하며 (이전의 매크로는 구현이 까다롭다), 컴파일러에게 최적화 기회를 제공한다. 

- 컴파일러에 의해 처리가 되면 무엇이 좋을까?
 : 바로 정의가 잘못될 시에 오류 메세지를 찍어 주는 역할을 해줄 수 있다는 것이다. 전처리기에 의해 처리 되는 매크로 함수는 만약 사용자의 실수로 정의가 잘못되었다고 하면, 이런 오류를 잡아내지 못하는 단점을 가지고 있다. 
- 구현의 용이 ?
 : 매크로 함수는 전처리 단계에서 기계적으로 인수를 치환하기 때문에 괄호를 잘 써줘야 하는데요, 반면 인라인 함수는 그냥 우리가 함수를 선언하고 사용하는것 처럼 아주 간단하게 사용할 수 있다는 점이 구현에 용이 하다고 할 수 있습니다. (아래는 사용의 예입니다. 앞에 간단히 inline만 붙여주면 돼죠) 
  1. inline int Square(int x)    
  2. {    
  3. return x * x;   
  4. }  

3. 네임스페이스 (Namespace)  
: 네임 스페이스는 이름 충돌을 막기 위해 공간에 이름을 주는 행위라고 할 수 있겠는데, 한 아파트에 철수가 두명 사는데 그냥 부르기는 헷갈리니 각 사는곳을 덧붙여 이름을 주는 것이죠. 101호에 사는 철수야 , 202호에 사는 철수야 하고 말이죠. 아래와 같이 간단히 쓸 수가 있습니다. 
  1. #include <iostream>   
  2.   
  3. namespace A_COM   
  4. {   
  5.     void function(void)   
  6.     {   
  7.         std::cout<<"A.com에서 정의한 함수"<<std::endl;   
  8.     }   
  9. }   
  10.   
  11. namespace B_COM   
  12. {   
  13.     void function(void)   
  14.     {   
  15.         std::cout<<"B.com에서 정의한 함수"<<std::endl;   
  16.     }   
  17. }   
  18.   
  19. int main(void)   
  20. {   
  21.     A_COM::function();   
  22.     B_COM::function();   
  23.     return 0;   
  24. }  
Posted by 모과이IT
,
1. 함수 오버로딩   
 : 함수 오버로딩(Function Overloading)이란 간단히 말하면, 동일한 이름의 함수를 중복해서 정의하는 것을 말합니다. (단, 매개변수의 정보가 달라야 한다는 가정하에 말입니다. 갯수나 타입이이 일치하지 않는 한도 내에서 말이죠)

C언어에서는 함수이름의 중복을 허용하지 않았는데, 그 이유는 C언어에서 함수를 호출할 때, 함수의 이름정보만을 가지고 호출할 함수를 찾기 때문입니다. 하지만, C++ 에서는 호출할 함수를 찾는데 있어서, 이름뿐만 아니라 매개변수의 정보도 포함하고 있기 때문에 이런 기능이 가능한 것입니다. 

아래와 같은 형태의 함수들은 C에서는 불가능했지만, C++에서는 이것이 가능하다는 얘기 입니다.
  1. int function (int n){}   
  2. int function (char c){}   
  3. int function (int n , char c){}  


2. 디폴트 매개변수  
: C++의 함수가 가진 또 하나의 특징은 바로 디폴트 매개변수(Default Parameter) 라는 것인데, 디폴트 매개변수란 전달되지 않는 인자를 대신하기 위한 기본값이 설정되어 있는 변수입니다. 아래와 같은 함수에서 (  ) 이 곳이 디폴트 매개변수를 선언할수 있는 부분입니다.
  1. int function (int a=0){ return a+1;}  

 - 사용법 : 만약 아래같이 분리 해서 사용할 경우 함수 정의 선언부에 한번만 설정 할 수 있습니다.
  1. #include<iostream>   
  2.   
  3. int BoxVolume(int length, int width=1, int height=1);   
  4.   
  5. int main()   
  6. {   
  7.     std::cout<<"[3, 3, 3]       : "<<BoxVolume(3, 3, 3)<<std::endl;   
  8.     std::cout<<"[5, 5, def]     : "<<BoxVolume(5, 5)<<std::endl;   
  9.     std::cout<<"[7, def, def]   : "<<BoxVolume(7)<<std::endl;   
  10.     return 0;   
  11. }   
  12.   
  13. int BoxVolume(int length, int width, int height)   
  14. {   
  15.     return length*width*height;   
  16. }  
Posted by 모과이IT
,