int num = 20
, int &ref=num
이 둘은 아래와 같이 선언 및 초기화 가능하다.int num(20); int &ref(num);
이 둘은 위와 결과적으로 동일하다.SimpleClass sim1(15, 20);
SimpleClass sim2(sim1);
도 가능하고 이때 두 객체간의 멤버 대 멤버 복사가 일어난다.(sim1-> sim2로 복사!)"복사 생성자에서 매개변수는 반드시 참조형이여야 한다."
const 선언은 필수가 아니지만 참조형 선언을 의미하는 &는 반드시 삽입해야 한다. 그이유는 나중에 알린다.
"복사 생성자를 정의하지 않는면, 멤버 대 멤버의 복사를 진행하는 디폴트 복사 생성자가 자동으로 삽입된다"
class SoSimple{
private:
int num1;
int num2;
public:
SoSimple(int n1, int n2) : num(n1), num(n2){}
SoSimple(const SoSimple ©) : num1(copy.num1), num2(copy.num2)//이 부분이 복사 생성자이며,
//따로 정의하지 않아도 자동으로 생성된다.
}
SoSimple sim1 = sim2// 이 구문은 자동으로 아래와 같이 묵시적 변환이 발생한다.
SoSimple sim1(sim2)// 이와 같은 형태로 묵시적 변환이 일어나서 복사 생성자가 호출된다.
explicit SoSimple(const SoSimple ©) : num1(copy.num1), num2(copy.num2)
{
//empty!
}
=
를 통해 객체의 생성 및 초기화가 불가능해진다. class SoSimple{
private:
int num1;
public:
SoSimple(int n1) : num(n1){}
};
class SoSimple test = 3 // SoSimple test(3)으로 묵시적 변환을 해준다.
explicit
키워드를 붙임으로써 해결 할 수 있다.class SimpleClass {
private:
int num1;
char * name;
public:
SimpleClass(const char *myname, int n2) {
int len = strlen(myname) + 1;
name = new char(len);
strcpy(name, myname);
num1 = n2;
}
int GetNum1() const {
return num1;
}
void showData() const {
cout << name << ' ' << num1 << endl;
}
SimpleClass& adder(int n) {
num1++;
return *this;
}
~SimpleClass() {
cout << "delete simpleclass" << endl;
delete []name; //할당 해제
}
};
int main(int argc, char** argv)
{
SimpleClass man1("myname", 29);
SimpleClass man2 = man1;
man1.showData();
man2.showData();
return 0;//
}
_____________
console:
myname 29
myname 29
delete simpleclass
@@@RUNTIME ERROR@@@
위와 같이 디폴트 복사 생성자를 쓰면 디폴트 복사 생성자에서 name(copy.name)
부분에 문제가 생긴다.
문자열을 복사해서 새로 할당하는게 아니라 같은 주소만 가르키게 되어, man1이나 man2가 해제되면 나머지 하나의 delete[] name에서 가리키고 있는 주소가 이미 해제 되어있으므로 런타임 에러가 나게된다.
SimpleClass (const SimpleClass& copy): num1(copy.num1)
{
name = new char[strlen(copy.name)+1];
strcpy(name, copy.name);
}
"복사 생성자의 호출 횟수는 프로그램의 성능과도 관계가 있기 때문에, 호출의 시기를 이해하는 것은 매우 중요하다."
"함수가 값을 반환하면, 별도의 메모리 공간이 할당되고, 이 공간에 반환 값이 저장된다.(반환 값으로 초기화된다.)"
int num1 = num2;// num1이라는 이름의 메모리 공간을 할당과 동시에 num2에 저장된 값으로 초기화시킨다.
____
int SimpleFunc(int n){...}
int main(void)
{
int num=10;
SimpleFunc(num);// 함수가 호출되는 순간 매개변수 n이 할당과 동시에 초기화!(int n = num)처럼 됨
}
____
int SimpleFunc(int n){...
return n;// 반환하는 순간 메모리 공간이 할당되면서 동시에 초기화! int가 아닌 객체일때도 마찬가지!
}
int main(void)
{
int num=10;
cout << SimpleFunc(num) << endl;// SimpleFunc에서 반환한 n값을 별도의 메모리 공간에 할당되어서
// 저장된다. 그리고 그 값을 출력한다. 객체일때도 마찬가지!
}
____
SoSimple obj2 = obj1; //이 경우도 위의 첫번째 구문과 같이 할당과 동시에 초기화도 이루어진다.
const
객체와 const
객체의 특성들const SoSimple sim(20)
에서 보이는 것 처럼 객체를 대상으로 const
선언이 붙게 되면, 이 객체를 대상으로는 const
멤버함수만이 호출이 가능하다.const
선언은 "이 객체의 데이터 변경을 허용하지 않겠다!."라는 뜻이다.const
와 함수 오버로딩const
함수도 오버로딩이 가능하다.
class SimpleClass {
private:
int num1;
char * name;
public:
SimpleClass(const char *myname, int n2) {
int len = strlen(myname) + 1;
name = new char(len);
strcpy(name, myname);
num1 = n2;
}
void SimpleFunc() {
cout << "simple func" << endl;
}
void SimpleFunc() const {
cout << "const simple func" << endl;
}
....
}
int main(int argc, char** argv)
{
const SimpleClass constSC("asd", 10);
SimpleClass SC("asdaasd", 110);
SC.SimpleFunc(); // -> simple func 출력
constSC.SimpleFunc();// const simple func 출력
}
위 코드를 통해 const
객체가 아니면 일반 simple func
을 부르고 const
객체일땐, const simple func
을 부르는 걸 알 수 있다.
friend
선언friend
선언friend
선언을 하면, B클래스는 A 클래스의 private
멤버에 직접 접근 가능하다.private
멤버에 직접 접근이 가능 하려면, B클래스가 A 클래스를 대상으로 friend
선언을 해줘야 한다.friend
선언은 언제?'정보 은닉'
을 무너뜨리는 문법이기 때문에 조심이 사용하자"friend 선언은 지나치면 아주 위험할 수 있습니다. friend 선언은 필요한 상황에서 극히 소극적으로 사용해야 합니다."
friend
선언static
static
void counter()
{
static int cnt; // 아래 main이 여러번 불려도 딱 한번만 생성되고 함수가 끝나도 사라지지 않는다
cnt++; // static은 초기화하지 않으면 0으로 초기화 되므로 main for문의 호출에 따라 0부터 ~10까지 증가한다.
cout << cnt << endl;
}
int main(void){
for(int i =0; i < 10; i++){
counter();// 함수 호출 // cnt 0 -> 10
}
return 0;
}
data
가 있을 때 필요하다.static
멤버변수(클래스 변수)클래스 변수
라고도 한다. 일반적인 멤버변수와 달리 클래스당 하나씩만 생성되기 때문이다.class SimpleClass{
public:
...
static int simObjCnt;
...
}
int SoSimple::simObjCnt=0;// static 멤버변수의 초기화 -> 안하고 바로 갖다쓰면 0으로 초기화된거 사용
static
멤버변수가 있는것이 아니라 객체 외부에 있다. 다만 객체에게 멤버변수처럼 접근할 수 있는 권한을 줬을 뿐이다.static
멤버변수의 또 다른 접근방법static
멤버변수는 어디서든 접근이 가능한 변수이다.public
으로 선언되면 클래스의 이름 또는 객체의 이름을 통해서 어디서든 접근 가능하다.private
으로 선언되면 해당 클래스의 객체들만 접근이 가능해진다.public static
멤버변수를 객체를 통해 멤버변수에 접근하듯이 접근하면 static
변수가 아닌 일반 멤버변수로 접근 하는 것처럼 보이니 지양하자.Simple Class sim1(10); int n = sim1.simObjCnt
"public static 멤버에 접근할 때에는 클래스의 이름을 이용해서 접근하는 것이 좋다"
static
멤버함수기본적으로 그 특성이 static
멤버변수와 동일하다.
선언된 클래스의 모든 객체가 공유한다.
public
으로 선언이 되면, 클래스의 이름을 이용해서 호출이 가능하다.
객체의 멤버로 존재하는 것이 아니다.
"static 멤버함수 내에서는 static 멤버변수와 static 멤버함수만 호출이 가능하다"
이러한 static 멤버변수와 static 멤버함수를 통해 전역변수와 전역함수를 대체할 수 있다.
const static
멤버앞서 ch04에서 보였듯이, 클래스 내에 선언된 const
멤버변수(상수)의 초기화는 이니셜라이저를 통해야만 한다. 그러나 const static
으로 선언되는 멤버변수(상수)는 다음과 같이 선언과 동시에 초기화가 가능하다.
class CountryArea{
public:
const static int RUSSIA =1707540;
const static int CANADA = 123909;
const static int KOREA = 12387;
const static int CHINA = 2984719;
};
const static
상수는 하나의 클래스에 둘 이상 모이는 것이 보통이다.const static
멤버변수는, 클래스가 정의될 때(객체가 생성될때가 아님!) 지정된 값이 유지되는 상수이기 때문에, 위 예제에서 보이는 바와 같은 방법으로 초기화가 가능하도록 문법으로 정의하고 있다.mutable
mutable은 아래와 같은 성질을 가진다.
"const 함수 내에서의 값의 변경을 예외적으로 허용한다."
mutable
의 과도한 사용은 C++에 잇어서 그 중요성을 인정받은 키워드인 const
의 선언을 의미없게 만들어버린다.