관리 메뉴

기억을 위한 기록들

C와 C++ 본문

C & CPP

C와 C++

에드윈H 2020. 12. 21. 19:26

생성자와 소멸자

생성자

- 생성자는 객체가 생성되면 자동으로 호출.

- 생성자가 정의되어 있지 않으면 컴팡일러는 기본 생성자라고 불리는 생성자를 자동으로 만든다.

- 클래스 기본 변수를 초기화하고 싶을때는 실제 객체가 만들어 ㅈ지기전, 생성자 코드의 나머지 부분이 실행되기 전에 값을 할당. ( 상수 혹은 클래스형 필드를 초기화 할때 유용

소멸자

- 객체가 소멸될 때 자동으로 호출. 객체를 삭제하는 작업을 담당.

- 인자를 전달할 수 없다.(명시적으로 호출 할 수 있는 메서드가 아니므로)

 



가상함수

ex)

class Person{
	...
    void aboutMe(){
    	cout<<"I am a Person.";
    }
}


class Student : public Person{
	...
    void aboutMe(){
    	cout<<"I am a student.";
    }
}

이런 예제가 있다고 치자

 

Student * p = new Student();
p->aboutMe();  //1번


Person * p = new Student();
p->aboutMe(); //2번

1번 같은 경우는 "I am a student."가 출력 될 것이다.

 

2번은 어떨까? "I am a Person."이 출력 될것이다.

 

왜냐하면 해당 함수는 어떤타입인지 컴파일시간에 결정되기 때문이다. 이런 매커니즘은 정적바인딩(static binding) 이라고한다.

 

만일 Student 클래스에서 구현된 aboutMe()를 호출 하고 싶다면, Person클래스에서 해당함수에 virtual을 붙여주면 된다.

class Person{
	...
    virtual void aboutMe(){ //virtual 추가
    	cout<<"I am a Person.";
    }
}


class Student : public Person{
	...
    void aboutMe(){
    	cout<<"I am a student.";
    }
}

 

이 외에 부모클래스에 어떤 메서드를 구현해 둘수 없는(혹은 구현하고 싶지않은) 경우에도 가상함수를 사용한다.

그리고 부모 클래스에서 자손클래스에서 사용 할 함수 내용을 아무것도 정의해 놓지 않는게 순수가상함수을 사용하여 구현은 하위 클래스들에게 맡겨야한다.

 

 

class Person{
	...
    virtual void aboutMe(){ //가상함수
    	cout<<"I am a Person.";
    }
    
    virtual bool addCourse(string s) = 0; //순수가상함수
};


class Student : public Person{
	...
    void aboutMe(){
    	cout<<"I am a student.";
    }
    
    bool addCourse(string s){
    	cout<<"Added course "<<s<<" to Student."<<endl;
    	return true;
    }
};

 

addCourse를 순수 가상 함수로 선언하였으므로, Person 클래스는 스스로 객체를 만들어 낼 수 없는 추상클래스가 된다.

 


가상소멸자

- 개념은 가상 함수의 개념에서부터 자연스럽게 유도된다. 

위 Person과 Student 클래스의 소멸자를 구현한다면 다음과 같이 할 수 있을것이다.

 

class Person{
	...
public:
	~Person()
    {
    	//"Person 삭제"
    }
};


class Student : public Person{
	...
	...
public:
	~Student()
    {
    	//"Student 삭제"
    }
};


int main()
{
	Person * p= new Student();
	delete p; //5번줄 "Person 삭제" 호출
}

 

그런데 p의 타입은 Person이다. 객체를 삭제 할때는 Person의 소멸자가 호출된다. 따라서 Student에 배정된 메모리가 제대로 반환되지 않을 수 있으므로, 문제가 발생 할 수있다.

 

이 문제를 고치기위해 Person의 소멸자를 가상 소멸자로 선언하면 된다.

 

class Person{
	...
public:
	virtual ~Person() //virtual 추가
    {
    	//"Person 삭제"
    }
};


class Student : public Person{
	...
	...
public:
	~Student()
    {
    	//"Student 삭제"
    }
};


int main()
{
	Person * p= new Student();
	delete p; //이렇게 되면 15번줄 "Student 삭제" 호출후, 5번줄 "Person 삭제" 호출
}

연산자 오버로딩

'+' 같은 연산자를 객체간의 연산에 사용 가능 BookShelf 객체 두개를 하나로 합치고 싶을 땐, '+'연산자를 다음과 같이 오버로딩하면 된다. 

BookShelf BookShelf::operator+(BookShelf &other){...}

 

포인터와 참조

-포인터는 변수의 주소를 담는 변수. (변수의 값 읽거나, 변경하기 등) 변수에 적용 가능한 연산은 모두 포인터를 통해 할 수 있다.

- 두 포인터가 같은 주소를 가리키는 경우에 한 포인터가 가리키는 변수의 값을 변경하면 다른 포인터가 가리키는 변수의 값도 바뀐다.

- 포인텨 분수의 크기는 아키텍쳐에 따라 달라진다. 32비트 컴퓨터에서는 32비트, 64비트 컴퓨터에서는 64비트가 된다.


참조(reference)

- 기존에 존재하는 객체에 붙는 또 다른 이름.

- 별도의 메모리를 갖지 않는다.

int a =5;
int &b = a;
b= 7
cout<<a<<endl //7 출력

b를 변경하면 a도 바뀐다. 참조 대상 메모리가 어딘지를 명시하지 않고, 참조를 만드는 방법은 없다.

하지만, 다음과 같이 독립적인 참조(free-standing)를 만둘 순 있다.

const int &b=12;

- 포인터와 달리 참조는 null이 될 수 없으며, 다른 메모리에 재할당 될수 없다.

 


포인터 연산

아래와 같이 포인터에 덧셈 연산을 하는것을 자주보게 된다.

int * p = new int[2];
p[0]=0; p[1]=1;
p++;
cout<<*p; //출력 1

p++를 실행하면, p는 sizeof(int) 바이트만큼 나아간다. 그래서 위는 1이 된다. p가 다른 타입이었다면, 해당 타입의 크기만큼 나아간다.

 

 

 

클래스 생성시 생기는 기본 함수들(암시적)

- 매개변수 없는 생성자

- 복사 생성자

- 소멸자

- 대입(=)연산자

'C & CPP' 카테고리의 다른 글

C표준 라이브러리 - 퀵정렬 qsort() 함수  (0) 2020.12.27
wchar_t 란??  (0) 2020.12.25
STL의 3가지 컨테이너 어댑터  (0) 2020.08.20
STL의 5가지 반복자(iterator)  (0) 2020.08.20
STL에서의 함수객체 2가지  (0) 2020.08.19