객체 배열 및 객체 포인터 배열은 C언어의 구조체 배열, 구조체 포인터 배열과 비슷하다.
객체 배열
객체 기반의 배열 선언하는 법을 본다.
SomSimple arr[10];
// SoSimple은 클래스 이름
동적 할당 객체 배열 선언
열 개의 SoSimple 객체가 모여서 배열을 구성하는 형태이다.
구조체 배열과 형태가 같다.
배열을 선언하는 경우도 생성자는 호출된다
SoSimple * ptrArr=new SomSimple[10];
단 배열 선언과정에서는 호출할 생성자를 별도로 명시하지 못한다. (생성자에 인자를 전달하지 못한다)
위 형태로 배열이 생성되려면 SoSimple() { .... } 형태의 생성자가 반드시 있어야 한다.
SoSimple() { .... }
배열 선언 이후 각각의 요소를 원하는 값으로 초기화시키려면 일일히 초기화 과정을 거쳐야 한다.
이전 예제의 Person 클래스 기반의 예제로 객체 배열을 살펴본다.
객체 배열 예제
객체 배열 생성 시 void형 생성자가 호출되며, 배열 소멸시에도 그 배열을 구성하는 객체의 소멸자가 호출된다.
// ObjArr.cpp
#define _CRT_SECURE_NO_WARNINGS
#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]; //Person 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;
}
객체 포인터 배열
객체 배열은 객체로 이뤄진 배열,
객체 포인터 배열은 객체의 주소값 저장이 가능한 포인터 변수로 이뤄진 배열이다.
객체 포인터 배열 예제
// ObjPtrArr.cpp
#define _CRT_SECURE_NO_WARNINGS
#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]; // 객체의 주소값 3개를 저장할 수 있는 포인터 배열 선언
char namestr[100];
int age;
for (int i = 0; i < 3; i++) { //정보를 입력받아 객체 초기화
cout << "이름 : ";
cin >> namestr;
cout << "나이 : ";
cin >> age;
parr[i] = new Person(namestr, age); //객체를 생성 후 객체의 주소값을 배열에 저장
}
parr[0]->ShowPersonInfo();
parr[1]->ShowPersonInfo();
parr[2]->ShowPersonInfo();
delete parr[0];
delete parr[1];
delete parr[2];
return 0;
}
parr[i]=new Person(namestr, age); 객체를 생성 후 객체의 주소값을 배열에 저장
객체를 저장할 때에는 두가지 중 하나로 저장한다. (객체를 저장 vs 객체의 주소값을 저장)
this 포인터
멤버함수 내에서는 this 포인터를 사용할 수 있다.
this 포인터는 객체 자신을 가리키는 용도로 사용되는 포인터이다.
this 포인터 이해 예제
this는 객체자신의 주소값을 의미한다.
this포인터는 주소값과 자료형이 정해지지 않은 포인터다.
// PointerThis.cpp
#define _CRT_SECURE_NO_WARNINGS
#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 객체의 주소 값 저장
// 객체 sim1에 의해 반환된 this는 SoSimple의 포인터이므로 SoSimple형 포인터변수에 저장
cout << ptr1 << ", "; //ptr1에 저장된 주소값 출력
ptr1->ShowSimpleData();
SoSimple sim2(200);
SoSimple* ptr2 = sim2.GetThisPointer(); //sim2 객체의 주소 값 저장
cout << ptr2 << ", ";
ptr2->ShowSimpleData();
return 0;
}
this 포인터의 활용
같은 이름의 함수 매개변수, 객체 멤버변수를 this포인터를 활용하면 의도대로 사용할 수 있다.
객체를 참조하는 포인터 this의 특성 때문이다.
this->num=207;
// PointerThis.cpp
#include<iostream>
using namespace std;
class TwoNumber
{
private:
int num1;
int num2;
public:
TwoNumber(int num1, int num2)
{
// 멤버변수 각각에 매개변수 각각을 저장한다.
this->num1 = num1;
this->num2 = num2;
}
// 멤버 이니셜라이저는 this 포인터를 사용할 수 없는 대신
// 저장하는 변수는 멤버변수로, 저장되는 값은 매개변수로 인식해서
// 이렇게도 사용할 수 있다.
/* TwoNumber(int num1, int num2)
* : num1(num1), num2(num2)
{
//empty
}*/
void ShowTwoNumber()
{
cout << this->num1 << endl; //this포인터는 생략 가능
cout << this->num2 << endl;
}
};
int main(void)
{
TwoNumber two(2, 4);
two.ShowTwoNumber();
return 0;
}
점심먹고 여기부터
Self-Reference의 반환
Self-Reference는 객체 자신을 참조할 수 있는 참조자이다.
this포인터를 사용해서 객체가 자신의 참조에 사용할 수 있는 참조자의 반환문을 구성할 수 있다.
// SelfRef.cpp
#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; //객체 자신의 포인터가 아닌 객체 자신을 반환
// this가 객체를 가리키는 포인터니까
// this가 가리키는 곳 (포인터의 포인터) 즉 객체자신.
}
SelfRef& ShowTwoNumber()
{
cout << num << endl;
return *this;
}
};
int main(void)
{
SelfRef obj(3);
SelfRef& ref = obj.Adder(2); //참조자므로 obj에 별명붙인거
obj.ShowTwoNumber();
ref.ShowTwoNumber(); //그래서 같은 값
ref.Adder(1).ShowTwoNumber().Adder(2).ShowTwoNumber(); //객체의 참조값을 반환해서 함수를 호출해서 가능
return 0;
}
참조의 정보(참조 값)에 대한 이해
변수 num을 참조할 수 있는 참조 값이 참조자 ref에 전달되어, ref가 변수 num 을 참조하게 된다.
int main(void)
{
int num=7;
int &ref=num; //변수 num을 참조할 수 있는 참조의 정보가 전달된다.
...
}
'C, C++ > 열혈 C++ 프로그래밍' 카테고리의 다른 글
05.복사생성자(copy constructor) (책) (0) | 2021.09.29 |
---|---|
05.복사생성자 (0) | 2021.09.28 |
04-03. 생성자와 소멸자(책) (0) | 2021.09.24 |
04. 클래스의 완성 연습문제 (0) | 2021.09.23 |
3. 클래스의 기본 (0) | 2021.09.15 |