메모 (작성 중)
1. value type( char, int, long ... ) 포인터에 new 키워드로 메모리 할당 시
char* s = new char[500];
char* ss = new char[500]();
char* ss = new char[500]{};
위의 세 줄의 코드의 차이는 괄호의 유무이다.
괄호가 없으면 할당된 메모리의 내용을 건드리지 않는다.
빈괄호가 붙으면 할당된 메모리의 내용을 모두 0으로 초기화 한다.
C언어 에서는 괄호 (Initializer) 를 인식하지 못한다
문자열을 생성할 때 빈괄호 Initializer를 사용하면 마지막에 null을 추가하지 않아도 된다.
아래는 { } 괄호를 이용한 직접 초기화 방식이다.
int* sss = new int[500]{ 1,2,3,4,5,6,7,1,2,3, };
2. 논리연산자는 다중 정의해서는 안된다.
다중 정의할 경우 사용자에게 심각한 논리적 오류를 떠넘기는 것이다.
단, const가 있고, 없고의 차이만 있는 경우 다중 정의를 해도 된다.
class TestBlock {
public:
const char& operator[](std::size_t position) const
{ return text[position]; }//상수 객체에 대한 operator[]
char& operator[](std::size_t position)
{ return text[position]; }//비상수 객체에 대한 operator[]
private:
std::string text;
};
위의 선언된 TextBlock의 operator[] 는 다음과 같이 쓸 수 있다.
TextBlock tb("Hello");
std::cout << tb[0];//비상수 멤버 호출
const TextBlock ctb("World");
std::wcout << ctb[0];//상수 멤버 호출
ctb[0]='x'; // 컴파일 에러 : 상수 객체에 대한 쓰기 불가
3. C++의 접근 지정자의 의미 ( public, protected, private )
1) protected 멤버에 대한 접근
C++의 Access specifier인 public, protected, private는 C#과 약간 다르므로 혼동 주의해야한다.
특히, protected 속성이 부여된 멤버는 Base class 와 Derived class의 멤버 함수에서만 접근이 가능하다.
class BaseClass {
public:
BaseClass() {
cout << "BaseClass()" << endl;
}
int GetData() {
Print();
return data;
}
protected://Base Class or Derived Class 의 내부에서만 접근 가능
void Print() {
cout << "data : " << data << endl;
}
private:
int data = 0;
};
class DerivedClass :protected BaseClass {//protected로 상속 시 Base class 의 protected 권한 이상 멤버만 내부에서 접근 가능
public:
DerivedClass() {
cout << "DerivedClass()" << endl;
}
};
위와같은 클래스가 있을 경우 protected 멤버 함수 Print()는 사용자에서 접근이 불가능하다(노출되지 않는다).
2) Protected 상속 관계와 형변환
파생클래스를 부모 클래스에 대입하는 것을 (업캐스팅)Up-casting 이라고 부른다.
그러나 파생 클래스와 부모 클래스의 상속관계가 public이 아닌 경우(protected, private) 얘기가 좀 달라진다.
이런 관계에서는 일반적으로 묵시적 형변환, static_cast, dynamic_cast, const_cast 가 불가능하다.
따라서 강제 형변환(C 스타일 형변환) 혹은 reinterpret_cast를 사용해야한다.
하지만 C 스타일의 강제 형변환은 많은 오류를 낳는다.
참고 - C++ 스타일 형 변환 4가지
4. 전위 연산자와 후위 연산자 오버로딩
전위 연산자와 후위 연산자는 매개변수가 있고 없고의 차이가 있다.
int operator++() { //매개변수 없으면 전위 연산자로 정의
return ++data;
}
int operator++(int) {//매개변수로 연관 type을 전달하면 후위 연산자로 정의
int nData = data;
data++;
return nData;
}
이처럼 후위 연산자는 임시 객체를 생성해서 반환하므로 오버헤드가 좀더 크다.
따라서 Iterator 사용 시 Pre-Increment/Decrement operator를 사용하는 이유도 여기에 있다.
std::array<int, 5>arr1 = { 1,2,3,4,5 };
for (auto iter = arr1.begin(); iter != arr1.end(); ++iter) {
std::cout << *iter << endl;
}
연산자 오버로딩 cppreference 참고 - en.cppreference.com/w/cpp/language/operators
operator overloading - cppreference.com
Customizes the C++ operators for operands of user-defined types. [edit] Syntax Overloaded operators are functions with special function names: operator op (1) operator type (2) operator new operator new [] (3) operator delete operator delete [] (4) operato
en.cppreference.com
5. 클래스 생성자 호출 순서
상속 관계에서 파생클래스의 생성자는 먼저 호출되지만 실행은 나중에 된다.
즉, 상위 클래스의 생성자부터 실행된다.
6. std::cin 숫자만 입력받기
숫자만 입력받고 싶은데 엉뚱한 문자를 입력하는 것에 대한 예외처리를 하려면 피곤해진다.
간단하게 하는 방법이 있다.
cin의 오버로딩된 연산자 ! 를 사용하여
int a=0;
while (a == 0) {
cout << "숫자를 입력해주세요 : ";
cin >> a;
if (!cin) {
cout << "숫자가 아닙니다!!\n";
cin.clear();
cin.ignore(INT_MAX, '\n');
}
}
7. std::getline 개행문자 버리기
프로그램에서 입력을 받을 때 Enter키를 눌러 입력을 종료한다.
이때 Enter 키는 입력 버퍼에 줄바꿈 문자 '\n'으로 추가된다.
입력 받을 때 >> 연산자를 통해 스트림 입력 추출을 사용하는 경우 모든 공백 문자는 읽고 버리게된다.
하지만 공백을 포함하는 문자열을 입력하고 싶은 경우 std::getline을 사용하게 된다.
그런데 std::getline을 사용할 경우 마지막에 줄바꿈 문자 '\n'이 남는다.
이때 마지막 개행문자 하나 때문에 이것을 빈 줄로 인식해버린다.
std::ws 를 사용하여 이것을 막는 트릭이 있다.
string psuname;
getline (cin >> ws, psuname);
트릭을 원치않는다면 다른 방법을 사용하자.
다음은 변수에 입력을 받을 때 개행이 스트림에 남아있는 것을 막는 방법이다.
std::istream::ignore 을 사용하며 문자를 추출하고 버릴 수 있다.
#include <limits>
int x;
char str[80];
cout << "Enter a number and a string:\n";
cin >> x;
cin.ignore(numeric_limits<streamsize>::max(), '\n'); //clear buffer before taking new line
cin.getline(str, 80); //take a string
cout << "You have entered:\n";
cout << x << endl;
cout << str << endl;
8.
연산자 오버로딩에서