일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- map
- 선택정렬
- C++최적화
- 정렬알고리즘
- 스마트포인터
- 프로그래머스
- 언리얼엔진구조체
- 언리얼가비지컬렉터
- 알고리즘
- 강참조
- BFS
- 애셋로드
- 람다사용정렬
- moreeffectiveC++
- 람다
- unorder_map
- 데이터애셋
- UELOG
- stl
- 약참조
- enumasByue
- 크리티컬섹션
- 델리게이트
- UML관련
- C++
- 자료구조
- UE4 커스텀로그
- dataasset
- UE_LOG
- 정렬
- Today
- Total
기억을 위한 기록들
C와 C++ 본문
생성자와 소멸자
생성자
- 생성자는 객체가 생성되면 자동으로 호출.
- 생성자가 정의되어 있지 않으면 컴팡일러는 기본 생성자라고 불리는 생성자를 자동으로 만든다.
- 클래스 기본 변수를 초기화하고 싶을때는 실제 객체가 만들어 ㅈ지기전, 생성자 코드의 나머지 부분이 실행되기 전에 값을 할당. ( 상수 혹은 클래스형 필드를 초기화 할때 유용
소멸자
- 객체가 소멸될 때 자동으로 호출. 객체를 삭제하는 작업을 담당.
- 인자를 전달할 수 없다.(명시적으로 호출 할 수 있는 메서드가 아니므로)
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
가상함수
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 |