2011. 3. 7. 21:06
학번같은 변하지 않는 상수 정보를 안정적으로 프로그래밍 하려면
const int id; //학번
의 방식으로 const키워드를 쓰는 것이 좋다.
하지만 다음과 같이 프로그래밍을 할 경우, 컴파일에서 오류가 발생하게 된다.

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

class Student
{
 const int id; //학번
 int age;//나이
 char name[20]; //이름
 char major[30]; //학과
public:
 
Student(int _id, int _age, char* _name, char* _major)
 {
  id=_id;
  age=_age;
  strcpy(name,_name);
  strcpy(major,_major);
 }
 void ShowData()
 {
  cout<<"이름: "<<name<<endl;
  cout<<"나이: "<<age<<endl;
  cout<<"학번: "<<age<<endl;
  cout<<"학과: "<<age<<endl;
 }
};
int main()
{
 Student Kim(20051577, 20, "Kim Gil Jung", "Computer Science");
 Student Hong(20091122,20,"Hong mi wyoun", "Architecture");

 Kim.ShowData();
 cout<<endl;
 Hog.ShowData();

}


오류를 살펴보자.
우리가 객체를 생성할 때 객체의 생성순서중 첫번째 단계는 '메모리 할당'이다.
이때 객체의 멤버 변수를 위한 메모리 공간도 할당되면서, id 값은 쓰레기 값으로 할당될 것이다.
따라서 이미 한번 초기화 되었으므로 생성자를 통한 초기화는 허용되지 않는다. 값의 변경으로 인식하기 때문이다.

이때 해결책!!
"멤버 이니셜라이저(member initializer)"이다.!
이것을 사용하면 const 멤버 변수를 초기화 하는 것이 가능하다.

  Student(....) : id(_id)
  => "멤버 변수 id를 매개 변수 _id로 초기화하라"

 둘 이상의 const 멤버도 ,(콤마) 연산자를 이용해서 구분 지어 초기화가 가능하다.

수정예제는 다음과 같다.

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

class Student
{
 const int id; //학번
 int age;//나이
 char name[20]; //이름
 char major[30]; //학과
public:
 Student(int _id, int _age, char* _name, char* _major) : id(_id)
 {
  age=_age;
  strcpy(name,_name);
  strcpy(major,_major);
 }
 void ShowData()
 {
  cout<<"이름: "<<name<<endl;
  cout<<"나이: "<<age<<endl;
  cout<<"학번: "<<age<<endl;
  cout<<"학과: "<<age<<endl;
 }
};
int main()
{
 Student Kim(20051577, 20, "Kim Gil Jung", "Computer Science");
 Student Hong(20091122,20,"Hong mi wyoun", "Architecture");

 Kim.ShowData();
 cout<<endl;
 Hong.ShowData();

}



참고: 열혈강의 c++


Posted by Triany
2011. 3. 7. 17:54
<복사생성자가 호출되는 시점>
1. 기존에 생성된 객체로 새로운 객체를 초기화하는 경우
2. 함수 호출 시 객체를 값에 의해 전달하는 경우
3. 함수 내에서 객체를 값에 의해 리턴하는 경우



1. 기존에 생성된 객체로 새로운 객체를 초기화하는 경우

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

class AAA
{
public:
 AAA()
 {
  cout<<"AAA() 호출"<<endl;
 }
 AAA(const AAA& a)
 {
  cout<<"AAA(const A& a) 호출"<<endl;
 }
};
int main()
{
 AAA obj1;
 AAA obj2=obj1; //AAA ojb2(obj1)이라는 문장으로 묵시적으로 변환됨

 return 0;
}



2. 함수 호출 시 객체를 값에 의해 전달하는 경우
call-by-value 함수 호출 과정
1) 매개 변수를 위한 메모리 공간 할당
2) 전달 인자 값의 복사
  => 복사생성자의 호출을 통해서 이 과정을 처리

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

class AAA
{
public:
 int val;
 AAA()
 {
  cout<<"AAA() 호출"<<endl;
 }
 AAA(int i)
 {
  val=i;
 }
 AAA(const AAA& a)
 {
  cout<<"AAA(const A& a) 호출"<<endl;
  val=a.val;
 }
 void ShowData()
 {
  cout<<"val: "<<val<<endl;
 }
};
void function(AAA a)
{
 a.ShowData();
}
int main()
{
 AAA obj1(30);
 function(obj1);

 return 0;
}




3. 함수 내에서 객체를 값에 의해 리턴하는 경우
- 리턴되는 값은 받아주는 변수가 없더라도, 함수를 호출한 영역으로 복사되어 넘어간다.
- 호출한 영역으로 객체가 복사되어 넘어갈 것.!

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

class AAA
{
public:
 int val;
 AAA()
 {
  cout<<"AAA() 호출"<<endl;
 }
 AAA(int i)
 {
  val=i;
 }
 AAA(const AAA& a)
 {
  cout<<"AAA(const A& a) 호출"<<endl;
  val=a.val;
 }
 void ShowData()
 {
  cout<<"val: "<<val<<endl;
 }
};
AAA function(void)
{
 AAA a(10);
 return a;
}
int main()
{
 function(); //function().ShowData();

 return 0;
}





출처: 열혈강의 C++프로그래밍
Posted by Triany
2011. 3. 7. 17:32
아래와 같이 변수를 동적할당 해 줄 경우,
명시적으로 복사생성자를 정의해 주어야 한다.
=>생성자 내에서 동적할당을 하면 반드시 제공해야 하는 것은 소멸자이다. 소멸자가 있어야 메모리 누수(유출)이 발생하지 않는다.
뿐만 아니라 복사 생성자도 정의해야 한다. 그래서 메모리 참조를 막을 수 있다.

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

class Person
{
 char *name;
 char *phone;
 int age;
public:
 Person(char* _name, char* _phone, int _age);
 Person(const Person& p);
 ~Person();
 void ShowData();
};
Person::Person(char* _name, char* _phone, int _age)
{
 name = new char[strlen(_name)+1];
 strcpy(name, _name);
 phone = new char[strlen(_phone)+1];
 strcpy(phone, _phone);
 age = _age;
}

Person::Person(const Person& p)

 name = new char[strlen(p.name)+1];
 strcpy(name, p.name);
 phone = new char[strlen(p.phone)+1];
 strcpy(phone, p.phone);
 age = p.age;
}

Person::~Person()
{
 delete[] name;
 delete[] phone;
}
void Person::ShowData()
{
 cout<<"name : "<<name<<endl;
 cout<<"phone: "<<phone<<endl;
 cout<<"age  : "<<age<<endl;
}
int main()
{
 Person p1("KIM", "013-333-5555", 22);
 Person p2=p1; //Person p2(p1);
 return 0;
}


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

멤버 이니셜라이저의 필요성 _const 멤버 변수를 초기화  (0) 2011.03.07
복사생성자가 호출되는 시점  (0) 2011.03.07
객체 포인터 배열  (0) 2011.03.04
생성자와 동적할당  (0) 2011.03.04
new / delete  (0) 2011.03.03
Posted by Triany
2011. 3. 4. 23:56

객체 포인터 배열 : 객체를 가리킬 수 있는 포인터로 구성이 되어 있는 배열

예제(new와 delete를 이용해서 어떻게 객체를 동적으로 생성 및 소멸하는지 알게 될것.)

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

class Point{
 int x;
 int y;
public :
 Point()
 {
  cout<<"Point() call!"<<endl;
  x=y=0;
 }
 Point(int _x, int _y)
 {
  x=_x;
  y=_y;
 }
 int GetX(){ return x;}
 int GetY(){ return y;}
 void SetX(int _x){ x=_x; }
 void SetY(int _y){ y=_y; }
};

int main()
{
 Point* arr[5];

 for(int i=0; i<5; i++)
 {
  arr[i] = new Point(i*2, i*3); //new에 의한 객체 동적 생성
 }
 for(int j=0; j<5; j++)
 {
  cout<<"x:  "<<arr[j]->GetX()<<' ';
  cout<<"y:  "<<arr[j]->GetY()<<endl;;
 }
 for(int k=0; k<5; k++)
 {
  delete arr[k]; //arr[k]가 가리키는 객체 소멸
 }
}


"new Point(i*2, i*3)"의 의미
:i*2와 i*3을 인자로 받을 수 있는 Point 클래스의 생성자를 호출하면서, Point 객체를 생성하라

=> 힙 영역에 생성. 생성된 객체의 주소 값이 Point*(Point 클래스의 포인터)형으로 반환될 것.

C에서 사용한 malloc 함수를 이용해서 객체 생성을 할 수 있을까? 없다!!
malloc 함수는 함수 호출 시 전달되는 인자만큼 단순히 메모리 공간만 할당하는 함수이기 때문이다. 즉 객체의 크기만큼 메모리 공간을 할당할 수는 있을 지라도, 생성자 호출은 이뤄지지 않는다. C++에서 이야기하는 객체의 조건을 만족시키려면, 생성자 호출은 반드시 이뤄져야 한다.
Posted by Triany
2011. 3. 4. 23:23
생성자와 동적할당

#include <iostream>
using std::cout;
using std::cin;
using std::endl;

const int SIZE=20;

class Person
{
 char *name;
 char *phone;
 int age;

public:
 Person(char* _name, char* _phone, int _age);
 void ShowData();

};
Person::Person(char* _name, char* _phone, int _age)
{
 
name = new char[strlen(_name)+1];
 strcpy(name, _name);
 phone = new char[strlen(_phone)+1];
 strcpy(phone, _phone);
 age = _age;
}

void Person::ShowData()
{
 cout<< " name: "<<name<<endl;
 cout<< " phone: " <<phone<<endl;
 cout<< " age: " << age<<endl;
}

int main()
{
 Person p("KIM", "013-113-1113 ", 22);
 p.ShowData();
 return 0;
}


결과적으로 객체 p는 main함수 내에서 생성되었으므로, 스택(stack)영역에 할당이 되겠지만, 생성자 내에서 메모리 공간을 동적을 할당하고 있기 때문에, 멤버 변수 name과 phone이 가리키는 메모리 공간은 힙(heap)이 된다.

=>이러한 형태의 초기화가 주는 이점은 메모리 공간을 효율적으로 사용할 수 있다는 것이다.


!!!문제 :!! 메모리 누수(유출)현상
=>해결 ? 소멸자에서 동적 해체!(delete)

 #include <iostream>
using std::cout;
using std::cin;
using std::endl;

const int SIZE=20;

class Person
{
 char *name;
 char *phone;
 int age;

public:
 Person(char* _name, char* _phone, int _age);
 
~Person();
 void ShowData();

};
Person::Person(char* _name, char* _phone, int _age)
{
 name = new char[strlen(_name)+1];
 strcpy(name, _name);
 phone = new char[strlen(_phone)+1];
 strcpy(phone, _phone);
 age = _age;
}

Person::~Person()
{
 
delete []name;
 delete []phone;
}

void Person::ShowData()
{
 cout<< " name: "<<name<<endl;
 cout<< " phone: " <<phone<<endl;
 cout<< " age: " << age<<endl;
}

int main()
{
 Person p("KIM", "013-113-1113 ", 22);
 p.ShowData();
 return 0;
}

!!!!!생성자 내에서 메모리를 동적 할당하는 경우, 이를 해제하기 위해서 반드시 소멸자를 정의해야 한다.!!




Posted by Triany
2011. 3. 3. 22:07
new : 힙 영역에 메모리 할당
<int 형 데이터 1개 저장>
int * arr = new int;

<길이가 size인 int형 배열 저장>
int * arr = new int[size];

NULL포인터를 리턴하는 new연산자
: 메모리 공간의 여의치 않다면(메모리를 할당만 하고, 적절히 소멸해 주지 않을 경우에 발생) new연산자에 의한 메모리 할당이 실패로 돌아갈 수도 있는 일. 이러한 경우 new 연산자는 NULL 포인터를 리턴..
NULL 포인터란 정수 0을 의미. 매크로로 정의되어 있는 상수 NULL을 사용해도 되고, 직접 0을 사용할 수도 있다.






delete 메모리 해제
<데이터 1개>
delete val;

할당된 메모리 공간이 배열인 경우, 2차 배열이건 3차배열이건 배열을 해제 할 때
delete[] arr;
이라 써주면 된다.


 #include <iostream>

using std::cout;
using std::cin;
using std::endl;

int main(void)
{
 //int형 데이터 1개 저장을 위한 메모리 할당
 int *val = new int;


 //길이가 size인 int형 배열을 위한 메모리 할당.
 int *arr = new int[size];
 ....

delete val;

delete []arr;

}


Posted by Triany
2011. 3. 3. 15:41
레퍼런스 선언
int val =10;
int &ref = val;


포인터를 이용한 Call-By-Reference 
: 포인터를 이용해서 포인터가 가리키는 메모리 공간에 직접 접근이 가능..
[단점] 포인터는 포인터 연산만 가능하기 때문에 잘못된 메모리 접근을 할 가능성이 높다.

 #include <iostream>

using std::cout;
using std::cin;
using std::endl;

void swap(int *a, int *b)
{
 int temp=*a;
//a++; // error.
 *a=*b;
 *b=temp;
}

int main(void)
{
 int val1=10;
 int val2=20;

 cout<< "val1: "<<val1<< "  ";
 cout<< "val2: "<<val2<<endl;

 swap(&val1, &val2);
 cout<< "val1: "<<val1<< "  ";
 cout<< "val2: "<<val2<<endl;
 return 0;
}




레퍼런스를 이용한 Call-By-Reference
:함수내에서 외부에 존재하는 변수에 직접 접근하는 것을 허용하되, 포인터 연산을 못하게 하는 방법.
 =>레퍼런스 사용
->swap함수 내에서는 a와 b라는 이름으로 main함수 내에 선언된 변수 val1과 val2에 직접 접근이 가능하게 된다.

 #include <iostream>

using std::cout;
using std::cin;
using std::endl;

void swap(int &a, int &b)
{
 int temp=a;
 a=b;
 b=temp;
}

int main(void)
{
 int val1=10;
 int val2=20;

 cout<< "val1: "<<val1<< "  ";
 cout<< "val2: "<<val2<<endl;

 swap(val1, val2);
 cout<< "val1: "<<val1<< "  ";
 cout<< "val2: "<<val2<<endl;
 return 0;
}






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

복사생성자가 호출되는 시점  (0) 2011.03.07
깊은 복사(Deep Copy) ..(const Person& p) 명시적 정의  (0) 2011.03.07
객체 포인터 배열  (0) 2011.03.04
생성자와 동적할당  (0) 2011.03.04
new / delete  (0) 2011.03.03
Posted by Triany