Api design for c++ 6장
-
Upload
ji-hun-kim -
Category
Education
-
view
1.160 -
download
0
Transcript of Api design for c++ 6장
6장. C++의올바른사용API design for C++
1
김지훈아꿈사
2014. 02. 15.
이번장은
대체로 C++의문법적측면의내용(pass)
C++을쓰면서놓치기쉬운 부분
API 제작시특히유의할부분
2
네임스페이스
접두어
glBegin(), GL_BLEND_COLOR, QWidget …
namespace 키워드
namespace MyAPI {
class String {
public: String();
....
}
}
3
네임스페이스
이름공간분리이름충돌방지
추상화도구
이름을연관된것끼리모음
4
생성자와할당연산자
The Big Three
소멸자, 복사생성자, 할당연산자
이중하나를정의하면일반적으로나머지도따라온다.
기본생성함수제어 (C++11)class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator = (const NonCopyable&) = delete;
};
5
const 메서드
객체의상태를변경하지않음을명시
논리적관점에서변화가없는경우변경할수도있음
mutable 키워드
6
mutable 키워드
Class HashTable{
public:
void Insert(const std::string &str);
int Remove(const std::string &str);
bool Has(const std::string &str) const;
int GetSize() const;
. . .
private:
int mCachedSize;
bool mSizeIsDirty;
};
7
int HashTable::GetSize() const {
// 성능을위해캐싱
if (mSizeIsDirty)
{
// Error!!
mCachedSize = CalculateSize();
mSizeIsDirty = false;
}
return mCachedSize;
}
mutable 키워드
Class HashTable{
public:
void Insert(const std::string &str);
int Remove(const std::string &str);
bool Has(const std::string &str) const;
int GetSize() const;
. . .
private:
mutable int mCachedSize;
mutable bool mSizeIsDirty;
};
8
int HashTable::GetSize() const {
// 성능을위해캐싱
if (mSizeIsDirty)
{
// OK!
mCachedSize = CalculateSize();
mSizeIsDirty = false;
}
return mCachedSize;
}
const 파라미터
std::string StringToLower(std::string &str);
str은 input 인가 output 인가?
Google c++ style guide
std::string StringToLower(const std::string &str); input parameter
std::string StringToLower(std::string* str); output parameter
9
const 리턴값
// return by value
std::string GetName() const
{
return mName;
}
// return by const reference
const std::string &GetName() const
{
return mName;
}
10
리턴값이객체의내부를변경할수없으므로안전
더나은성능필히 const 선언필요리턴된값의수명주기확인필요
가급적 return by value를이용하자
template
template <typename T>
class Stack {
public:
void Push(T val);
T Pop();
bool IsEmpty() const;
private:
std::vector<T> mStack;
};
11
Stack<int> int_stack;int_stack.Push(1);int_stack.Push(2);int_stack.Push(3);
Stack<std::string> str_stack;str_stack.Push("123");str_stack.Push("456");
암시적인스턴스화
헤더파일에템플릿클래스의 구현코드를모두포함
API 사용자가원하는어떤타입이라도인스턴스화할수있음
구현소스가헤더에모두노출됨
12
암시적인스턴스화
소스의노출을최소화하기위해헤더가아닌별도의파
일에구현
13
// stack_priv.htemplate<typename T>T Stack<T>::Pop() {
...}
// stack.h
template<typename T>
class stack {
T Pop();
...
};
#include "stack_priv.h”
명시적인스턴스화
API개발자가미리지정해둔타입만생성가능
14
// stack.h
template<typename T>
class stack {
T Pop();
...
};
// stack.cpptemplate<typename T>T Stack<T>::Pop() {
...}
// 명시적인스턴스화template class Stack<int>;template class Stack<double>;template class Stack<std::string>;
명시적인스턴스화
컴파일타임감소
구현코드숨김
extern template (C++11)
15
extern template
16
// header.htemplate<typename T> void ReallyBigFunction() { // Body }
// source1.cpp#include "header.h"void something1() { ReallyBigFunction<int>(); }
// source2.cpp#include "header.h"void something2() { ReallyBigFunction<int>(); }
source1.o void something1() void ReallyBigFunction<int>() // Compiled first time
source2.o void something2() void ReallyBigFunction<int>() // Compiled second time <- 중복컴파일!
extern template
17
source1.o void something1() void ReallyBigFunction<int>() // Compiled first time
source2.o // No ReallyBigFunction<int> here because of the extern
// source2.cpp#include "header.h“
extern template ReallyBigFunction<int>(); void something2() { ReallyBigFunction<int>(); }
컴파일타임감소
오브젝트사이즈감소
// 현재오브젝트에서인스턴스화금지
정적 lib 형태로라이브러리배포할때 lib안에서쓰면유용할듯
함수기본파라미터의단점
class Circle {public:
Circle(double x = 0, double y = 0, double radius = 10.0);. . .
};
Circle c1();Circle c2(2.3);Circle c3(2.3, 5.6); Circle c4(2.3, 5.6, 1.5);
18
// x만입력하고 y는없는논리적모순// 반지름이변경되면 Client Code 재컴파일필요
기본값대신함수오버로드
class Circle {
public:
Circle();
Circle(double x, double y); // 반지름정보가 cpp에숨겨짐
Circle(double x, double y, double radius);
. . .
};
19
#DEFINE 상수사용금지
#define MORPH_FADEIN_TIME 0.3f
#define MORPH_IN_TIME 1.1f
#define MORPH_FADEOUT_TIME 1.4f
20
#DEFINE 상수사용금지
타입이없음
#define MAX_VALUE 1 // 정수형
#define MAX_VALUE 1f // 부동소수형
실수의여지가있음
21
#DEFINE 상수사용금지
범위가없음
#define max(a, b) a > b ? a : b
class Array {public:
Array();int max(); // Compile error!...
}
전역네임스페이스를오염시킴
22
#DEFINE 상수사용금지
접근제어불가능
public, protected, private 설정안됨
항상 public
심볼이없음
매크로는전처리과정에서사라지기때문에디버깅어려움
23
상수변수를사용하자
class Morph {
public:
static const float FadeInTime;
static const float InTime;
static const float FadeOutTime;
. . .
};
float fade_in_time = Morph::FadeInTime;
24
enum을사용
#define LEFT_JUSTIFIED 0
#define RIGHT_JUSTIFIED 1
#define CENTER_JUSTIFIED 2
#define FULL_JUSTIFIED 3
enum JustificationType {LEFT_JUSTIFIED,RIGHT_JUSTIFIED,CENTER_JUSTIFIED,FULL_JUSTIFIED
};
25
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
friend 사용금지
캡슐화의구멍을허용함 (여기까지저자의생각)
반면,
인터페이스는전역에공개되지만 friend는한정된공개이므로더
캡슐화될수도있음.
27
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
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
더나은구조
#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
꼭나쁘기만한건없다
a : 어차피멤버에접근이필요하다면 get/set 보다낫지않냐?
b : 하지만애초에그럴수밖에없는구조가나쁜건아닐까?
a : 그럼구조를뒤집어야되는데?
더나쁜설계에서더좋은설계로개선해가는리펙토링의
중간단계로서의미는있음. (개인적의견)
31
CPP도안전하지않다
// xxx.cpp. . .const int INTERNAL_CONSTANT 42;std::string Filename = "file.txt";void FreeFunction(){
std::cout << "Free function called" << std::endl;}const int MAX_VALUE = 100;. . .
32
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
CPP도안전하지않다
정적선언// library cpp
static const int INTERNAL_CONSTANT 42;
static std::string Filename = “file.txt”;
static void FreeFunction() { …}
익명네임스페이스namespace {
const int INTERNAL_CONSTANT 42;std::string Filename "file.txt";void FreeFunction() {
std::cout << "Free function called" << std::endl;}
}34
코딩규칙
C++은강력하지만아주복잡
C++을잘활용하려면필요한만큼언어의기능을제약해야함복잡하지만쓰고싶은만큼덜어쓸수있는게장점
코딩규칙의첫번째는일관성
google c++ style guide
35
Refference
API design for C++, Martin Reddy
template : http://stackoverflow.com/questions/8130602/using-extern-
template-c0x
friend : http://ikpil.com/1036
36