5. 생성자와 할당 연산자
The Big Three
소멸자, 복사 생성자, 할당 연산자
이중 하나를 정의하면 일반적으로 나머지도 따라온다.
기본 생성 함수 제어 (C++11)
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator = (const NonCopyable&) = delete;
};
5
6. const 메서드
객체의 상태를 변경하지 않음을 명시
논리적 관점에서 변화가 없는 경우 변경할 수 도 있음
mutable 키워드
6
7. mutable 키워드
Class HashTable{
int HashTable::GetSize() const {
// 성능을 위해 캐싱
public:
void Insert(const std::string &str);
if (mSizeIsDirty)
int Remove(const std::string &str);
{
bool Has(const std::string &str) const;
// Error!!
int GetSize() const;
mCachedSize = CalculateSize();
...
mSizeIsDirty = false;
private:
}
int mCachedSize;
bool mSizeIsDirty;
return mCachedSize;
}
};
7
8. mutable 키워드
Class HashTable{
int HashTable::GetSize() const {
// 성능을 위해 캐싱
public:
void Insert(const std::string &str);
if (mSizeIsDirty)
int Remove(const std::string &str);
{
bool Has(const std::string &str) const;
// OK!
int GetSize() const;
mCachedSize = CalculateSize();
...
mSizeIsDirty = false;
}
private:
return mCachedSize;
mutable int mCachedSize;
mutable bool mSizeIsDirty;
}
};
8
10. const 리턴 값
// return by value
std::string GetName() const
{
return mName;
}
// return by const reference
const std::string &GetName() const
{
return mName;
}
리턴값이 객체의 내부를 변경할 수
없으므로 안전
더 나은 성능
필히 const 선언 필요
리턴된 값의 수명주기 확인 필요
가급적 return by value를 이용하자
10
12. 암시적 인스턴스화
헤더파일에 템플릿 클래스의 구현 코드를 모두 포함
API 사용자가 원하는 어떤 타입이라도 인스턴스화 할 수 있음
구현 소스가 헤더에 모두 노출됨
12
13. 암시적 인스턴스화
소스의 노출을 최소화 하기 위해 헤더가 아닌 별도의 파
일에 구현
// stack.h
template<typename T>
class stack {
T Pop();
...
};
#include "stack_priv.h”
// stack_priv.h
template<typename T>
T Stack<T>::Pop() {
...
}
13
14. 명시적 인스턴스화
API개발자가 미리 지정해둔 타입만 생성 가능
// stack.h
template<typename T>
class stack {
T Pop();
...
};
// stack.cpp
template<typename T>
T Stack<T>::Pop() {
...
}
// 명시적 인스턴스화
template class Stack<int>;
template class Stack<double>;
template class Stack<std::string>;
14
17. extern template
// source2.cpp
#include "header.h“
extern template ReallyBigFunction<int>(); // 현재 오브젝트에서 인스턴스화 금지
void something2() { ReallyBigFunction<int>(); }
source1.o
void something1() void ReallyBigFunction<int>() // Compiled first time
source2.o
// No ReallyBigFunction<int> here because of the extern
컴파일 타임 감소
오브젝트 사이즈 감소
정적 lib 형태로 라이브러리 배포할때 lib안에서 쓰면 유용할 듯
17
18. 함수 기본 파라미터의 단점
class Circle {
public:
Circle(double x = 0, double y = 0, double radius = 10.0);
...
};
Circle c1();
// x만 입력하고 y는 없는 논리적 모순
Circle c2(2.3);
// 반지름이 변경되면 Client Code 재컴파일 필요
Circle c3(2.3, 5.6);
Circle c4(2.3, 5.6, 1.5);
18
19. 기본값 대신 함수 오버로드
class Circle {
public:
Circle();
Circle(double x, double y); // 반지름 정보가 cpp에 숨겨짐
Circle(double x, double y, double radius);
...
};
19
20. #DEFINE 상수 사용 금지
#define MORPH_FADEIN_TIME 0.3f
#define MORPH_IN_TIME 1.1f
#define MORPH_FADEOUT_TIME 1.4f
20
21. #DEFINE 상수 사용 금지
타입이 없음
#define MAX_VALUE 1 // 정수형
#define MAX_VALUE 1f // 부동 소수형
실수의 여지가 있음
21
22. #DEFINE 상수 사용 금지
범위가 없음
#define max(a, b) a > b ? a : b
class Array {
public:
Array();
int max(); // Compile error!
...
}
전역 네임스페이스를 오염시킴
22
23. #DEFINE 상수 사용 금지
접근 제어 불가능
public, protected, private 설정 안됨
항상 public
심볼이 없음
매크로는 전처리 과정에서 사라지기 때문에 디버깅 어려움
23
26. friend 사용 금지
class Node {
public:
...
friend class Graph;
private:
void ClearVisited();
void SetVisited();
bool IsVisited() const;
...
bool is_visited;
};
#include "node.h"
// 이름이 같은 별도의 Graph 클래스
Class Graph {
public:
void ViolateAccess(Node *node) {
// Graph는 node의 friend이므로
// private 멤버 호출 가능
node ->SetVisited();
}
};
26
27. friend 사용 금지
캡슐화의 구멍을 허용함 (여기까지 저자의 생각)
반면,
인터페이스는 전역에 공개되지만 friend는 한정된 공개이므로 더
캡슐화 될 수도 있음.
27
28. friend는 정말 나쁜가
class Node {
public:
...
friend class Graph;
private:
void ClearVisited();
void SetVisited();
bool IsVisited() const;
...
};
#include "node.h"
Class Graph {
public:
void ViolateAccess(Node *node) {
if (node -> IsVisited ()) {
……
}
}
};
28
29. friend는 정말 나쁜가
class Node {
public:
...
friend class Graph;
bool IsVisited() const; // 전역 공개
private:
void ClearVisited();
void SetVisited();
bool IsVisited() const;
...
};
#include "node.h"
Class Graph {
public:
void ViolateAccess(Node *node) {
if (node -> IsVisited ()) {
……
}
}
};
29
30. 더 나은 구조
#include "node.h"
// 이름이 같은 별도의 Graph 클래스
Class Graph {
public:
void ViolateAccess(Node *node) {
if (visited_nodes_.find(node) != visited_nodes_.end()) {
……
}
}
std::set<Node *> visited_nodes_;
};
30
31. 꼭 나쁘기만 한 건 없다
a : 어차피 멤버에 접근이 필요하다면 get/set 보다 낫지 않냐?
b : 하지만 애초에 그럴 수 밖에 없는 구조가 나쁜건 아닐까?
a : 그럼 구조를 뒤집어야 되는데?
더 나쁜 설계에서 더 좋은 설계로 개선해 가는 리펙토링의
중간 단계로서 의미는 있음. (개인적 의견)
31
33. CPP도 안전하지 않다
// 우리 라이브러리를 가져다 쓰는 Client Code.cpp
extern void FreeFunction();
extern const int INTERNAL_CONSTANT;
extern std::string Filename;
FreeFunction();
std::cout << "Constant " << INTERNAL_CONSTANT << std::endl;
Filename = "different.txt";
// 추가로 이름 충돌 문제도 있음.
const int MAX_VALUE = 100; // link time error!! (이름 중복)
33
35. 코딩 규칙
C++은 강력하지만 아주 복잡
C++을 잘 활용하려면 필요한 만큼 언어의 기능을 제약해야 함
복잡하지만 쓰고 싶은 만큼 덜어 쓸 수 있는게 장점
코딩 규칙의 첫 번째는 일관성
google c++ style guide
35
36. Refference
API design for C++, Martin Reddy
template : http://stackoverflow.com/questions/8130602/using-externtemplate-c0x
friend : http://ikpil.com/1036
36