Api design for c++ 6장

1,409
-1

Published on

Published in: Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,409
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Api design for c++ 6장

  1. 1. 6장. C++의 올바른 사용 API design for C++ 김지훈 아꿈사 2014. 02. 15. 1
  2. 2. 이번 장은 대체로 C++의 문법적 측면의 내용(pass) C++을 쓰면서 놓치기 쉬운 부분 API 제작시 특히 유의할 부분 2
  3. 3. 네임스페이스 접두어 glBegin(), GL_BLEND_COLOR, QWidget … namespace 키워드 namespace MyAPI { class String { public: String(); .... } } 3
  4. 4. 네임스페이스 이름 공간 분리 이름 충돌 방지 추상화 도구 이름을 연관된 것 끼리 모음 4
  5. 5. 생성자와 할당 연산자 The Big Three 소멸자, 복사 생성자, 할당 연산자 이중 하나를 정의하면 일반적으로 나머지도 따라온다. 기본 생성 함수 제어 (C++11) class NonCopyable { public: NonCopyable() = default; NonCopyable(const NonCopyable&) = delete; NonCopyable& operator = (const NonCopyable&) = delete; }; 5
  6. 6. const 메서드 객체의 상태를 변경하지 않음을 명시 논리적 관점에서 변화가 없는 경우 변경할 수 도 있음 mutable 키워드 6
  7. 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. 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
  9. 9. 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
  10. 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
  11. 11. template template <typename T> class Stack { public: void Push(T val); T Pop(); bool IsEmpty() const; private: std::vector<T> mStack; }; 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"); 11
  12. 12. 암시적 인스턴스화 헤더파일에 템플릿 클래스의 구현 코드를 모두 포함 API 사용자가 원하는 어떤 타입이라도 인스턴스화 할 수 있음 구현 소스가 헤더에 모두 노출됨 12
  13. 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. 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
  15. 15. 명시적 인스턴스화 컴파일 타임 감소 구현 코드 숨김 extern template (C++11) 15
  16. 16. extern template // header.h template<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 <- 중복 컴파일! 16
  17. 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. 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. 19. 기본값 대신 함수 오버로드 class Circle { public: Circle(); Circle(double x, double y); // 반지름 정보가 cpp에 숨겨짐 Circle(double x, double y, double radius); ... }; 19
  20. 20. #DEFINE 상수 사용 금지 #define MORPH_FADEIN_TIME 0.3f #define MORPH_IN_TIME 1.1f #define MORPH_FADEOUT_TIME 1.4f 20
  21. 21. #DEFINE 상수 사용 금지 타입이 없음 #define MAX_VALUE 1 // 정수형 #define MAX_VALUE 1f // 부동 소수형 실수의 여지가 있음 21
  22. 22. #DEFINE 상수 사용 금지 범위가 없음 #define max(a, b) a > b ? a : b class Array { public: Array(); int max(); // Compile error! ... } 전역 네임스페이스를 오염시킴 22
  23. 23. #DEFINE 상수 사용 금지 접근 제어 불가능 public, protected, private 설정 안됨 항상 public 심볼이 없음 매크로는 전처리 과정에서 사라지기 때문에 디버깅 어려움 23
  24. 24. 상수 변수를 사용하자 class Morph { public: static const float FadeInTime; static const float InTime; static const float FadeOutTime; ... }; float fade_in_time = Morph::FadeInTime; 24
  25. 25. enum을 사용 #define LEFT_JUSTIFIED #define RIGHT_JUSTIFIED #define CENTER_JUSTIFIED #define FULL_JUSTIFIED 0 1 2 3 enum JustificationType { LEFT_JUSTIFIED, RIGHT_JUSTIFIED, CENTER_JUSTIFIED, FULL_JUSTIFIED }; 25
  26. 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. 27. friend 사용 금지 캡슐화의 구멍을 허용함 (여기까지 저자의 생각) 반면, 인터페이스는 전역에 공개되지만 friend는 한정된 공개이므로 더 캡슐화 될 수도 있음. 27
  28. 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. 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. 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. 31. 꼭 나쁘기만 한 건 없다 a : 어차피 멤버에 접근이 필요하다면 get/set 보다 낫지 않냐? b : 하지만 애초에 그럴 수 밖에 없는 구조가 나쁜건 아닐까? a : 그럼 구조를 뒤집어야 되는데? 더 나쁜 설계에서 더 좋은 설계로 개선해 가는 리펙토링의 중간 단계로서 의미는 있음. (개인적 의견) 31
  32. 32. 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
  33. 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
  34. 34. 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
  35. 35. 코딩 규칙 C++은 강력하지만 아주 복잡 C++을 잘 활용하려면 필요한 만큼 언어의 기능을 제약해야 함 복잡하지만 쓰고 싶은 만큼 덜어 쓸 수 있는게 장점 코딩 규칙의 첫 번째는 일관성 google c++ style guide 35
  36. 36. Refference API design for C++, Martin Reddy template : http://stackoverflow.com/questions/8130602/using-externtemplate-c0x friend : http://ikpil.com/1036 36
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×