this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include<iostream>
using namespace std;
class MyClass {
public:
void printThis() {
cout << "나의 주소는:" << this << endl;
}
};
int main() {
MyClass a, b;
cout << "a의 주소는" << &a << endl;
cout << "b의 주소는" << &b << endl;
a.printThis();
b.printThis();
}
|
cs |
a.printThis는 a의 주소값과 같고, b.printThis는 b의 주소값과 같은 것을 확인할 수 있다.
그래서 this라는 애는 자신이 소속된 객체의 주소를 가지고 오는구나~ 라고 생각할 수 있다.
사실상 이 코드와 똑같다고 볼 수 있다. 그렇기에 this를 써준다는 것은 보이지 않는 매개변수가
있다는 것이고, 클래스나 struct 안에 있는 변수나 함수는 보이지 않는 매개변수로 this를 가지고 있다.
즉,this는 보이지 않는 매개변수이다!
객체의 생성과 소멸
생성자: 객체가 호출될 때 자동으로 호출되는 함수
소멸자: 객체가 소멸될 때 자동으로 호출되는 함수
먼저 생성자를 살펴보자.
생성자는 반환하는 자료형을 적어주지 않는다. 자바로 치면 public Myclass(){}; 와 똑같다고 보면된다.
물결표시가 소멸자이다.
전역객체를 만들어 생성자와 소멸자가 언제 실행되는지 살펴보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include<iostream>
using namespace std;
class Myclass {
public:
Myclass() {
cout << "생성자가 호출되었다!" << endl;
}
~Myclass() {
cout << "소멸자가 호출되었다!" << endl;
}
};
Myclass globalObj;
int main() {
cout << "main함수 시작!" << endl;
cout << "main 함수 끝!" << endl;
}
|
cs |
main함수가 시작되기도 전에 전역객체가 생성되어 생성자를 호출하고, main함수 끝나고 소멸자가 실행되는 것을 확인할 수 있다.
지역변수와 생성자,소멸자
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
31
|
#include<iostream>
using namespace std;
class Myclass {
public:
Myclass() {
cout << "생성자가 호출되었다!" << endl;
}
~Myclass() {
cout << "소멸자가 호출되었다!" << endl;
}
};
//Myclass globalObj;
void testLocalObj() {
cout << "testLocalObj함수 시작!" << endl;
Myclass localObj;
cout << "testLocalObj함수 끝!" << endl;
}
int main() {
cout << "main함수 시작!" << endl;
testLocalObj();
cout << "main 함수 끝!" << endl;
}
|
cs |
28번째 줄에 main함수 시작 출력 후 29번째 줄에 testLocalObj를 호출한다.
20번째 줄에 testLocalObj로 들어가서 localObj 객체를 생성한다. 객체가 생성되었으니
9번째 줄로 올라가 생성자가 호출되고, 23번째 줄로 내려와서 testLocalObj함수 끝을 출력 후
13번째 줄로 올라가 소멸자를 호출시키고 30번째 줄로 내려와서 main함수 끝을 출력한다.
지역변수를 선언할 경우 지역변수가 있는 공간, 지역변수 localObj의 경우에는
이 변수가 속한 testLocalObj 함수(지역변수가 속한 공간)가 끝나면 소멸자도 호출된다.
생성자의 기능
생성자는 멤버변수 초기화할때
+)소멸자는 메모리 해제할때 쓰인다.
복소수를 저장하는 클래스를 만들어보자
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#include<iostream>
using namespace std;
class Complex {
public:
Complex() {
real = 0;
imag = 0;
//둘다 0으로 초기화
}
//생성자 오버로딩
Complex(double real_, double imag_) {
real = real_;
imag = imag_;
}
double GetReal() {
return real;
}
void setReal(double real_) {
real = real_;
}
double GetImag() {
return imag;
}
void setImag(double imag_) {
imag = imag_;
}
private:
double real;
double imag;
};
int main() {
Complex c1;//매개변수가 없는 생성자만 실행됨
Complex c2 = Complex(2, 3); //매개변수가 있는 생성자 실행
Complex c3(2, 3);
cout << "c1: " << c1.GetReal() << "," << c1.GetImag() << endl;
cout << "c2: " << c2.GetReal() << "," << c2.GetImag() << endl;
cout << "c3: " << c3.GetReal() << "," << c3.GetImag() << endl;
}
|
cs |
Complex c1;
Complex c2 = Complex(2, 3);
--> 이런 방식이 C++에서 객체를 생성하는 방식이다.
c1은 생성자에서 매개변수로 아무것도 받지 않은 애(첫번째 생성자)가 호출되고 c2의 경우 첫번째 생성자를 오버로딩하여 매개변수 두개를 받는 생성자를 호출한다.
그렇기에 출력값을 보면 c1의 경우, 매개변수로 아무것도 받지 않아 0,0이 출력되고 c2는 매개변수로 2,3을 받았기 때문에 2,3이 출력된다.
c3는 c2와 같은데 표현방식만 다른거다.
여기서 내 개인적인 의문점이 생겼다.
보통 자바에서 객체 생성할 때는 Complex c2=new Complex(2,3); 이런식으로 선언하는데 C++에서는 new를 쓰니까 바로 에러가 떴다.
그 이유를 찾아보니 C++은 Complex c2 =Complex(2,3) 같이 new를 사용하지 않은 일반적인 변수
선언 방식을 이용하면 스택영역에 할당된다. 그리고 new 키워드를 사용하면 Complex *c2 = new Complex(2, 3);
와 같은데 이는 힙영역에 객체가 할당된것이다.
일반적인 변수 선언 방식으로 객체를 생성하면 해당 함수에서 벗어날 경우 메모리가 해제되는데
new 키워드를 이용하면 직접 delete해주기 전까지는 메모리가 해제되지 않아 함수에서 벗어나지
않아도 메모리가 유지된다.
여담으로 자바의 경우에는 new연산자가 객체 생성하는 역할을 담당하는데 자바는 new연산자를 통해 힙메모리에
데이터 저장할 공간을 할당받는다.
참고자료
https://blog.naver.com/PostView.nhn?isHttpsRedirect=true&blogId=heartflow89&logNo=220955262405
생성자의 여러가지 사용방법
생성자 오버로딩 한 애에 디폴트 매개변수를 2개 다 설정해주면
main 함수에서 c1객체 생성할때 에러가 뜬다. 왜냐하면 c1의 경우 매개변수를 안받는 첫번째 생성자에도 들어갈 수 있고, 오버로딩된 두번째 생성자에도 들어갈 수 있기 때문이다.(어차피 디폴트 매개변수가 0이기 때문에)
이럴때는 그냥 생성자를 하나로 합쳐주면 된다고 생각해볼 수 있다.
어차피 디폴트 매개변수를 통해 초기화 시켜줬기 때문에 이렇게 오버로딩된 형태를 써도 상관 없다.
초기화 목록
C++에서 변수를 선언할 때
int a=5; 를 int a(5) 이런식으로 선언하는데 이 방법을 생성자에도 똑같이 적용해볼거다.
Complex() {
real(0);
imag(0);
}
으로 바꿔볼 수 있는데 이걸 생성자 초기화 목록이라는 방법을 통해
Complex():real(0),imag(0) { } <--이렇게 바꿀 수 있다.
그리고 언더스코어 없이 매개변수와 같은 이름의 멤버변수를 만들 수도 있다!
생성자 위임
생성자들에 공통된 매개변수가 있을 때, 생성자 위임이라는 기능을 사용한다.
시간을 알려주는 프로그램을 만들려한다. 단위는 시,분,초 인데 예를 들어 5초 일경우 , 0시간 0분 5초로 생성자에서 받아주는게 아니라 그냥 5만 생성자의 매개변수로 받아주려 한다.
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
31
32
33
34
|
#include<iostream>
using namespace std;
class Time {
public:
Time() :h(0), m(0), s(0) {
}
Time(int s_) {
s = s_;
}
Time(int m_, int s_) {
m = m_;
s = s_;
}
Time(int h_, int m_, int s_) {
h = h_;
m = m_;
s = s_;
}
private:
int h;
int m;
int s :
};
|
cs |
생성자 3개에서 m_과 s_는 중복되어 들어간걸 확인 할 수 있다. 이때 생성자 위임을 해볼 것이다!
위에 생성자에 있었던 매개변수를 아래에 또 다시 쓰게 될 경우 생성자 위임을 통해 Time(s_),Time(m_,s_) 이런식으로 써주면 된다.
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
31
32
33
34
35
36
37
38
39
|
#include <iostream>
using namespace std;
class Time {
public:
Time() :h(0), m(0), s(0) {
}
Time(int s_) :Time() {
s = s_;
}
Time( int m_, int s_) :Time(s_) {
m = m_;
}
Time(int h_, int m_, int s_) :Time(s_, m_) {
h = h_;
}
int h;
int m;
int s;
};
int main() {
Time t1;
Time t2;
Time t3;
t1 = Time(5);
t2 = Time(2, 3);
t3 = Time(1, 3, 12);
cout << "t1: " << t1.h << ":" << t1.m << ":" << t1.s << endl;
cout << "t2: " << t2.h << ":" << t2.m << ":" << t2.s << endl;
cout << "t3: " << t3.h << ":" << t3.m << ":" << t3.s << endl;
}
|
cs |
최종코드는 위와 같다.
t1 객체를 생성할때 5초의 경우 t1=Time(0,0,5)로 하지 않고 t1=Time(5)로 해도 생성자 위임, 생성자 초기화를 통해 0시간 0분 5초라 출력되는 것을 볼 수 있다.
이처럼 생성자 위임을 사용하면 코드의 길이가 짧아지고, 수정하기 쉬워진다.
'C++ > 기초(두들낙서)' 카테고리의 다른 글
[C++기초] 상수형 매개변수와 상수형 메서드 (0) | 2022.01.14 |
---|---|
[C++기초] static (0) | 2022.01.14 |
[C++기초] 클래스 개념소개,접근제어 지시자,구조체와 클래스의 차이점 (0) | 2022.01.12 |
[C++기초] 네임스페이스 (0) | 2022.01.12 |
[C++기초]이차원 배열을 범위기반 for문으로 구현하기 (0) | 2022.01.12 |
댓글