# 이름 없는 namespace를 선언하고 그 안에 함수나 변수를 넣으면 namespace가 속한 파일 안에서만 사용할 수 있다.
마치 C의 static 키워드의 효과를 낸다.
namespace {
int val;
void func(){}
}
# scope가 다르다면 같은 이름의 변수를 사용할 수 있다. 그리고 변수의 값을 찾을 때는 가장 가까운 범위 내에서 찾는다.
#include <iostream>
int main() {
int a = 1;
{
std::cout << "a : " << a << std::endl;
int a = 2;
std::cout << "a : " << a << std::endl;
}
}
결과
a : 1
a : 2
# 인자가 없는 생성자를 호출할 때 아래와 같은 상황을 조심해야한다.
#include <iostream>
class Test {
public:
Test() {
std::cout << "Test" << std::endl;
}
};
int main() {
Test test = Test(); //명시적 방법
Test test2;
Test test(); //생성자에 인자가 없는 경우, 리턴값이 Test인 함수를 정의한 것으로 인식
}
# default 생성자를 사용하고 싶어, 개발자가 다른 생성자를 안만든 것인지 아니면 생성자 만드는 걸 까먹은 것인지 알 수 없기 때문에 아래와 같이 default 생성자를 의도한 것이라고 나타낼 수 있다.
class Test {
public:
Test() = default; // 디폴트 생성자를 정의해라
};
# 클래스의 이름과 namespace의 이름은 동일할 수 없다.
# 소멸자는 인자를 줄 수도 없고, 오버로딩도 할 수 없다. (그렇게 할 필요도 없다)
#You only need to use new if you are trying to allocate a object on the heap
#동일한 많은 인스턴스를 만들 때, 한 인스턴스마다 생성자에 값을 입력해주기는 번거롭다 (물론 loop를 돌리면서 해도되지만)
이럴때 복사 생성자를 사용하여 이미 생성된 인스턴스의 정보를 주입시켜버릴 수 있다. 복사 생성자는 '생성'시에만 호출된다.
class Test {
int a;
public:
Test();
Test(const Test &t);
Test(int _a);
};
Test::Test(const Test &t) {
a = t.a; //생성자 내부에서 t에 대한 정보를 고칠 수 없도록, const를 사용하여 방지한다.
}
Test::Test(int _a) {
a = _a;
}
int main() {
Test t1(1);
Test t2(t1); //t1의 레퍼런스를 사용한다.
Test t3 = t2; //컴파일러에서 복사생성자로 변환한다. Test t3(t2);
Test t4;
t4 = t3; //이건 복사 생성자가 아니라 그냥 복사해버리는 것. 복사 생성자는 '생성' 시 호출됨
}
사실 디폴스 생성자와 디폴스 소멸자가 있듯이, 디폴트 복사 생성자가 있기 때문에 복사 생성자를 구현 안해도 정상동작한다. 각 맴버 변수들을 대응시켜 넣어주는 코드를 컴파일러에서 추가해준다.
class Test {
int a;
public:
Test();
Test(const Test &t);
Test(int _a);
};
/*
Test::Test(const Test &t) {
a = t.a; //생성자 내부에서 t에 대한 정보를 고칠 수 없도록, const를 사용하여 방지한다.
}
*/
Test::Test(int _a) {
a = _a;
}
int main() {
Test t1(1);
Test t2(t1); //t1의 레퍼런스를 사용한다.
Test t3 = t2; //컴파일러에서 복사생성자로 변환한다. Test t3(t2);
Test t4;
t4 = t3; //이건 복사 생성자가 아니라 그냥 복사해버리는 것. 복사 생성자는 '생성' 시 호출됨
}
그러나 복사생성자로 여러개의 객체에서 같은 힙영역을 보는 상황에서 어떤 객체의 소멸자에서 그 영역을 delete해버리면, 다른 객체의 소멸자에서 에러가 발생한다. (Sigsegv)
이를 해결하기 위해 복사 생성자 내에서 '깊은 복사'를 통해 각각의 메모리 영역을 바라보게 해야한다. 즉, 디폴트 복사 생성자는 사용하기에 한계가 있다. (아래 링크의 디폴트 복사 생성자의 한계부분을 참고하면 좋다)
#초기화 리스트에서 클래스 맴버의 이름과 인자의 이름이 같아도 된다.
class Test {
int a;
int b;
public:
Test(int a, int b);
};
Test::Test(int a, int b)
:a(a), b(b){} //바깥쪽 a는 맴버변수의 a를 가리키고, 괄호 안 a는 인자를 알아서 가리킨다.
int main() {
Test t1(1, 2);
}
#초기화 리스트와 생성자 내부를 구현하는 것과의 차이는 초기화 리스트는 선언과 동시에 생성을 하는 것이고, 생성자 내부 구현은 선언 후 값을 대입해주는 것이다. 그렇다면 클래스 내부의 레퍼런스들은 선언과 생성이 동시에 진행되어야하므로 초기화 리스트를 사용해야한다.
상수도 마찬가지이다.
class Test {
int a;
const int b;
public:
Test(int a, int b);
};
Test::Test(int a, int b)
:a(a), b(b){} //b가 상수이고, 외부에서 인자로 받아오지만 선언과 동시에 초기화하므로 정상적으로 동작
int main() {
Test t1(1, 2);
}
#원래 C++에서는 맴버 변수를 클래스 안에서 초기화하는 것을 못했다. 초기화 리스트나 객체 생성자를 이용해서 초기화를 진행했어야했다. 하지만 맴버변수를 초기화하기 위해 초기화 리스트나 객체 생성자를 사용하는 것이 귀찮은 일이 될 수 있다. 그래서 클래스 안에서 바로 초기화할 수 있도록 허용하게되었다. 하지만 모든 맴버 변수를 클래스 안에서 직접 초기화할 수 있는 것은 아니다.
#맴버변수와 생성자의 매개변수의 이름이 동일하면 생성자 내에서는 그 이름을 매개변수로 인식한다.
#생성자 안에서 this 키워드를 사용하면 할당되지 않은 메모리에 접근한다고 에러 뜨는데, 생성자 호출이 완료되어야 객체가 메모리에 생성되는 것으로 보인다.
#상수 함수 안에는 상수 함수만 호출될 수 있다.
#include <iostream>
class A {
int data_;
public:
A(int data) : data_(data) {}
void DoSomething(int x) const {
change(); //상수 함수가 아니면 에러!
}
void change() const {}
};
int main() {
A a(10);
a.DoSomething(3);
'한 줄메모' 카테고리의 다른 글
[한 줄메모]Linux 쉘 스크립트 정리 (0) | 2021.03.17 |
---|---|
[한 줄메모] C언어 (0) | 2021.03.17 |