C++_OOP4
클래스의 특징과 객체 활용
const
const 멤버함수
const 사용 목적은 객체의 멤버변수를 변경시킬 수 없도록 하기 위함이다.
const 멤버함수는 객체의 멤버변수를 변경할 수 없는 읽지 전용함수로, const로 지정되지 않은 다른 함수도 호출할 수 없다.
int GetX() const
{
return this->x;
}
const 객체
객체를 상수로 취급하여 초기화된 데이터 외에 다른 데이터로 변경 불가능하게 만든다.
int main()
{
const MousePoint pt1(10, 20);
MousePoint pt2(100, 200);
pt1 = pt2;
}
pt1은 상수화 되었기 때문에 수정할 수 없다.
static
전역변수는 현재 클래스뿐 아니라 다른 클래스에서도 접근 가능해 객체지향 철학에 위배된다. static변수는 전역변수와 같은 성질의 멤버 함수이되, 외부 클래스에서는 접근할 수 없는 변수이다.
static 멤버변수
메모리 구조상 전역변수와 같은 공간인 데이터영역에 메모리가 할당. 전역변수와 비슷한 성질을 가지면서도 클래스에 국한되어 사용된다는 차이가 있다.
static으로 선언하면 클래스에 대한 객체가 생성되어도, 단 하나의 변수만 메모리에 할당된다. static변수가 private속성을 갖는다면 같은 클래스의 멤버함수에서만 접근 가능하고 클래스 외부함수에서는 접근이 불가능하다.
전역변수와 같이 프로그램 시작 시 한번만 초기화한다. 또한 접근지정자는 초기화 과정에 전혀 영향을 주지 않으므로, private속성의 static 멤버변수일지라도 클래스 외부에서 초기화 가능하다.
소속클래스명 ::
영역결정 연산자 표시해줌으로써 static멤버변수의 소속을 지정해준다
double Deposit::dInterestRate = 0.05;
#include <iostream>
using namespace std;
class Deposit
{
public:
Deposit() // 디폴트 생성자
{
}
Deposit(char *name, double balance) // 생성자(오버로딩)
{
strName = name;
dBalance = balance;
}
void BankBalnce()
{
dBalance = dBalance + (dBalance * dInterestRate);
}
static void SetInterestRate(double dNewRate)
{
dInterestRate = dNewRate;
}
private:
char *strName;
double dBalance;
static double dInterestRate;
};
double Deposit::dInterestRate = 0.05;
int main()
{
Deposit custom1;
Deposit custom2;
Deposit::SetInterestRate(0.03);
}
static 멤버함수
static 멤버변수의 값을 변경하기 위해 static 멤버 함수를 사용한다.
private 속성의 static 멤버변수 dInterestRate
에 접근하기 위해서 사용한다.
#include <iostream>
using namespace std;
class Deposit
{
public:
Deposit() // 디폴트 생성자
{
}
Deposit(char *name, double balance) // 생성자(오버로딩)
{
strName = name;
dBalance = balance;
}
void BankBalnce()
{
dBalance = dBalance + (dBalance * dInterestRate);
}
static void SetInterestRate(double dNewRate)
{
dInterestRate = dNewRate;
}
static double GetInterestRate()
{
return dInterestRate;
}
private:
char *strName;
double dBalance;
static double dInterestRate;
};
double Deposit::dInterestRate = 0.05;
int main()
{
cout << "변경 전 이자율 : " << Deposit::GetInterestRate() << endl;
Deposit::SetInterestRate(0.03); // 이자율 변경
cout << "변경된 이자율 : " << Deposit::GetInterestRate() << endl;
}
현재 이자율 : 0.05
변경된 이자율 : 0.03
static 멤버함수는 특정 객체에 영향을 미치지 않으므로 this포인터
를 사용하지 못한다.
또한 static 멤버함수 내에서 일반 멤버함수를 호출하지 못하는데, 일반 멤버함수는 객체를 통해서 관리되는 함수이기 때문이다.
객체 상태 정보 관리
static 멤버를 사용하여 객체의 상태 정보 관리에 사용 가능하다.
#include <iostream>
using namespace std;
class Deposit
{
public:
Deposit() // 디폴트 생성자
{
nCount++;
cout << "객체 개수(생성) : " << nCount << endl;
}
~Deposit()
{
nCount--;
cout << "객체 개수(소멸) : " << nCount << endl;
}
Deposit(char *name, double balance) // 생성자(오버로딩)
{
strName = name;
dBalance = balance;
}
void BankBalnce()
{
dBalance = dBalance + (dBalance * dInterestRate);
}
static void SetInterestRate(double dNewRate)
{
dInterestRate = dNewRate;
}
static double GetInterestRate()
{
return dInterestRate;
}
private:
char *strName;
double dBalance;
static double dInterestRate;
static int nCount;
};
double Deposit::dInterestRate = 0.05;
int Deposit::nCount = 0;
int main()
{
Deposit custom1;
Deposit custom2;
Deposit custom3;
Deposit custom4;
}
객체 개수(생성) : 1
객체 개수(생성) : 2
객체 개수(생성) : 3
객체 개수(생성) : 4
객체 개수(소멸) : 3
객체 개수(소멸) : 2
객체 개수(소멸) : 1
객체 개수(소멸) : 0
프렌드(Friend)
클래스의 멤버변수 접근 지정자는 private으로 외부로부터 접근을 차단한다. 오직 멤버함수를 통해서만 멤버변수에 접근이 가능한데, 이 규칙을 깨는 변칙적인 기능이 프렌드이다.(나에게 접근을 허용하겠다)
클래스의 동적 메모리 할당
new/delete
동적 메모리의 필요성
동적메모리는 실행시간에 할당되어 사용되는 메모리 블록을 말한다.(힙)
<->정적 메모리(스택)
동적 메모리는 프로그램 작성 시 얼마만큼의 메모리가 필요한지 알지 못하는 경우에 사용된다. 객체지향 언어에서는 동적 메모리 생성시 new
, 소멸시 delete
(C++) 연산자를 사용한다.
#include <iostream>
using namespace std;
int main()
{
int *pBuffer;
int nLength;
cout << "힙 영역에 할당할 메모리 수 :";
cin >> nLength;
pBuffer = new int[nLength];
for (int i = 0; i < nLength; i++)
pBuffer[i] = i + 1;
for (int i = 0; i < nLength; i++)
cout << pBuffer[i] << " ";
cout << endl;
delete[] pBuffer;
}
힙 영역에 할당할 메모리 수 : 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
힙 영역에 객체 생성
new
연산자를 통해 클래스 타입의 메모리를 생성 후 선언한 객체 포인터 변수에 생성 메모리 주소를 넘겨준다
int main()
{
MousePoint *pt;
pt = new MousePoint(10, 20);
cout << "x좌표 : " << pt->GetX() << endl;
cout << "y좌표 : " << pt->GetY() << endl;
delete pt;
}
x좌표 : 10
y좌표 : 20
객체 포인터 배열
여러 개의 객체를 관리하는 방법으로 배열을 선언하여 사용할 수 있는데, 이를 객체 포인터 배열이라고 한다.
int main()
{
MousePoint *pArr[3]; //1
pArr[0] = new MousePoint(10, 20);
pArr[1] = new MousePoint(100, 200);
pArr[2] = new MousePoint(1000, 2000);
for (int i = 0; i < 3; i++)
cout << pArr[i]->GetX() << ", " << pArr[i]->GetY() << endl;
for (int i = 0; i < 3; i++)
delete pArr[i]; //1
MousePoint* mp = new MousePoint[3]; //2
delete[] mp; //2
}
10, 20
100, 200
1000, 2000
//1 객체를 delete
//2 배열을 delete
포인터 멤버변수를 갖는 클래스
클래스 내의 동적 메모리 할당
#include <iostream>
using namespace std;
class String
{
public:
String(char ch, int nSize);
~String();
private:
int nLength;
char *pBuffer;
};
String::String(char ch, int nSize)
{
nLength = nSize;
pBuffer = new char[nLength + 1];
memset(pBuffer, ch, nLength);
pBuffer[nLength] = '\0';
cout << "pBuffer : " << pBuffer << endl;
cout << "nLength : " << nLength << endl;
}
String::~String()
{
delete[] pBuffer;
}
int main()
{
String str('A', 5);
}
pBuffer : AAAAA
nLength : 5
객체끼리의 대입
int main()
{
String str1('A', 3);
String str2('B', 5);
str1 = str2;
}
객체끼리의 대입할 때 객체끼리의 대입은 멤버 대 멤버의 복사가 일어난다.
str2.nLength = str1.nLength;
str2.pBuffer = str1.pBuffer;
-
대입 이전
-
대입 이후
같은 주소값을 갖는 것은 문제되지 않지만, 객체가 소멸할 때 stack메모리 중 str2객체가 먼저 소멸하는데, 소멸할 때 소멸자를 호출하면 str2의 pBuffer멤버변수가 가리키고 있는 힙 영역을 delete
하게 된다. 시스템으로 다시 반납
str1도 같은 메모리를 가르키고 있으므로 str1 객체 소멸시 pBuffer가 가리키는 메모리 영역을 다시 delete
하려 하는데, 이 메모리는 이미 비워지고 시스템에 반납되어 런타임에서 접근하지 못해 오류가 발생한다.
이 문제를 해결하기 위해 대입 연산자 오버로딩(Assignment Operator Overloading)
이라는 방법을 사용한다.
Author And Source
이 문제에 관하여(C++_OOP4), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@gimmicks_/cpp4저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)