일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 오블완
- LLM
- regression
- Classification
- deep learning
- supervised learning
- 머신러닝
- gpt
- LG Aimers
- LG Aimers 4th
- 회귀
- LG
- 티스토리챌린지
- 지도학습
- PCA
- OpenAI
- ChatGPT
- 해커톤
- GPT-4
- AI
- 분류
- Machine Learning
- 딥러닝
- Today
- Total
SYDev
C++ Chapter 04-4 : 클래스와 배열 그리고 this 포인터 본문
객체배열
객체 기반의 배열은 다음 형태로 선언한다.(SoSimple은 클래스의 이름)
SoSimple arr[10];
이를 동적으로 할당하는 경우에는 다음의 형태가 된다.
SoSimple * ptrArr = new SoSimple[10];
이렇게 배열을 선언하면, 열 개의 SoSimple 클래스의 객체가 모여서 배열을 구성하는 형태가 된다. 이렇게 객체 기반 배열을 선언할 때도 생성자는 호출이 되는데, 이 경우에는 생성자에 직접 인자를 전달하지 못한다는 문제점이 생긴다.
그렇기 때문에 위 형태의 배열을 선언하기 위해서는 다음과 같은 형태의 생성자가 선언되어 있어야 한다.
SoSimple() { . . . . }
이후에 각각의 요소를 원하는 값으로 초기화시키기 위해서는 각각의 객체에 별도로 초기화의 과정을 거쳐야 한다.
이전 예제에서 정의한 Person 클래스를 기반으로 해당 내용을 더 살펴보자.
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
private:
char * name;
int age;
public:
Person(char * myname, int myage)
{
int len=strlen(myname)+1;
name=new char[len];
strcpy(name, myname);
age = myage;
}
Person()
{
name = NULL;
age = 0;
cout<<"called Person()"<<endl;
}
void SetPersonInfo(char * myname, int myage)
{
name = myname;
age = myage;
}
void ShowPersonInfo() const
{
cout<<"이름 : "<<name<<", ";
cout<<"나이 : "<<age<<endl;
}
~Person()
{
delete []name;
cout<<"called destructor!"<<endl;
}
};
int main(void)
{
Person parr[3];
char namestr[100];
char * strptr;
int age;
int len;
for(int i=0; i<3; i++)
{
cout<<"이름 : ";
cin>>namestr;
cout<<"나이 : ";
cin>>age;
len = strlen(namestr)+1;
strptr = new char[len];
strcpy(strptr, namestr);
parr[i].SetPersonInfo(strptr, age);
}
parr[0].ShowPersonInfo();
parr[1].ShowPersonInfo();
parr[2].ShowPersonInfo();
return 0;
}
called Person()
called Person()
called Person()
이름 : 한지수
나이 : 21
이름 : 양은정
나이 : 31
이름 : 이한영
나이 : 34
이름 : 한지수, 나이 : 21
이름 : 양은정, 나이 : 31
이름 : 이한영, 나이 : 34
called destructor!
called destructor!
called destructor!
this 포인터의 이해
멤버함수 내에서는 this라는 이름의 포인터를 사용할 수 있고, 이는 객체 자신을 가리키는 용도로 사용된다.
다음 예제를 보면서 this 포인터를 이해해보자.
#include <iostream>
#include <cstring>
using namespace std;
class SoSimple
{
private:
int num;
public:
SoSimple(int n) : num(n)
{
cout<<"num = "<<num<<", ";
cout<<"address = "<<this<<endl;
}
void ShowSimpleData()
{
cout<<num<<endl;
}
SoSimple * GetThisPointer()
{
return this; // 이 문장을 실행하는 객체의 포인터를 반환
}
};
int main(void)
{
SoSimple sim1(100);
SoSimple * ptr1 = sim1.GetThisPointer(); // sim1 객체의 주소값 저장
cout<<ptr1<<", ";
ptr1 ->ShowSimpleData(); // ptr1이 가리키는 객체의 ShowSimpleData 함수를 호출
SoSimple sim2(200);
SoSimple * ptr2 = sim2.GetThisPointer(); // sim2 객체의 주소값 저장
cout<<ptr2<<", ";
ptr2 ->ShowSimpleData(); // ptr2이 가리키는 객체의 ShowSimpleData 함수를 호출
return 0;
}
num = 100, address = 0x61fe0c
0x61fe0c, 100
num = 200, address = 0x61fe08
0x61fe08, 200
예제 코드와 실행 결과에서 보이듯이 this는 객체 자신의 주소값을 의미한다.
this 포인터의 활용
this 포인터의 활용을 배우기 전에, 다음 클래스를 관찰해보자.
class ThisClass
{
private:
int num; // 207이 저장됨
public:
void ThisFunc(int num)
{
this -> num = 207;
num = 105; // 매개변수 값을 105로 변경함
}
. . . . .
};
해당 클래스에서 함수 ThisFunc의 매개변수 num은 클래스 ThisClass의 멤버변수 num과 이름이 같다. 함수 내에서 num은 매개변수 num을 의미하기 때문에 변수의 이름만으로는 멤버변수 num에 접근할 수 없다. 이럴때 this 포인터를 활용하면 멤버변수 num에 접근할 수 있다.
객체를 참조하는 포인터는 지역변수에 접근이 불가능하기 때문에 이러한 특성을 활용하여 멤버변수와 매개변수의 이름이 같더라도 각각의 변수에 적절하게 접근이 가능하다.
다음 예제를 통해 더 자세히 살펴보자.
#include <iostream>
using namespace std;
class TwoNumber
{
private:
int num1;
int num2;
public:
TwoNumber(int num1, int num2)
{
this -> num1 = num1; // this -> num1은 멤버변수
this -> num2 = num2; // this -> num2은 멤버변수
}
/*
TwoNumber(int num1, int num2)
:num1(num1), num2(num1) //이니셜라이저에서는 this 포인터 사용 불가능, but 형태가 "멤버변수(매개변수)"
{
//empty
}
*/
void ShowTwoNumber()
{
cout<<this -> num1<<endl;
cout<<this -> num2<<endl;
}
};
int main(void)
{
TwoNumber two(2, 4);
two.ShowTwoNumber();
return 0;
}
2
4
Self-Reference의 반환
Self-Reference란 객체 자신을 참조할 수 있는 참조자를 의미한다. 우리는 this 포인터를 이용하여 자신의 참조에 사용 가능한 참조자의 반환문을 구성할 수 있다.
이와 관련된 다음 예제를 확인하자.
#include <iostream>
using namespace std;
class SelfRef
{
private:
int num;
public:
SelfRef(int n) : num(n)
{
cout<<"객체 생성"<<endl;
}
SelfRef& Adder(int n) // 객체 자신의 참조의 정보(참조값)을 반환
{
num +=n;
return *this; // 객체 자신의 값
}
SelfRef& ShowTwoNumber() // 객체 자신의 참조의 정보(참조값)을 반환
{
cout<<num<<endl;
return *this; // 객체 자신의 값
}
};
int main(void)
{
SelfRef obj(3);
SelfRef &ref = obj.Adder(2); // 함수 Adder의 반환값이 자신의 참조값이기 때문에 ref는 obj를 참조
obj.ShowTwoNumber();
ref.ShowTwoNumber();
ref.Adder(1).ShowTwoNumber().Adder(2).ShowTwoNumber();
return 0;
}
객체 생성
5
5
6
8
참조의 정보(참조값)에 대한 이해
위에서 언급한 참조의 정보란 무엇일까? 우선 다음 코드를 확인해보자.
int main(void)
{
int num = 7;
int &ref = num; // 무엇이 전달된다고 표현할까?
. . . .
위 코드에서 ref에 전달하는 것은 무엇이라고 표현해야 할까? 우선 num에 저장된 정수값은 아니다. 그렇기 때문에 대입 연산에 어울리게 다음과 같이 표현하기도 한다.
"변수 num을 참조할 수 있는 참조의 정보가 전달된다."
다시 말해 변수 num을 참조할 수 있는 참조값이 참조자 ref에 전달되어, 참조자 ref가 변수 num을 참조하게 되는 것이다.
출처 : 윤성우, <윤성우의 열혈 C++ 프로그래밍>, 오렌지미디어, 2010.05.12
'Programming Lang > C++' 카테고리의 다른 글
C++ Chapter 05-2 : '깊은 복사'와 '얕은 복사' (0) | 2023.07.17 |
---|---|
C++ Chapter 05-1 : 복사 생성자(Copy Constructor) (1) | 2023.07.16 |
C++ Chapter 04-3 : 생성자(Constructor)와 소멸자(Destructor) 문제풀이 (0) | 2023.07.16 |
C++ Chapter 04-3 : 생성자(Constructor)와 소멸자(Destructor)(2) (0) | 2023.07.16 |
C++ Chapter 04-3 : 생성자(Constructor)와 소멸자(Destructor)(1) (0) | 2023.07.16 |