코딩 스타일 지침서
1. 목적
1.1. 필요성
코딩 컨벤션(convension)을 준수하면 소스 코드의 일관성(consistency) 이 유지되어 가독성(readability)을 높여주고 유지 보수를 용이하게 한다. 또, 나쁜 코딩 스타일을 피하여 실수로 인한 버그 발생을 감소시켜 소프트웨어 품질을 향상시킨다.
1.2. 주요 사항
C++은 C에서 C with class, Template Programming, Generic Programming, Metaprogramming, functional style programming 등으로 점차 진화, 발전되었다. 하지만 generic, metaprogramming, functional 등 일부 추가된 기술은 가독성을 떨어뜨리는 경향이 있으므로 선별적으로 사용한다.
표준(C++03~C++20)의 기능을 사용하며 C++17, C++20의 기능을 프로젝트에 사용할 때는 이식성을 고려해야 한다.
처음 세 개 절인 포맷팅, 네이밍, 주석은 스타일 위주로 정의된 반면, 다음 절부터는 C++ 프로그램 구조에 영향을 주는 규칙이 포함되었다.
2. 포맷팅 가이드
2.1. 포맷팅 (Formatting)
2.1.3. 함수 선언과 정의
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2)
{
DoSomething();
...
}
-
리턴타입은 함수명과 같은 라인에 있어야 한다.
-
함수명과 ‘(‘ 사이, 파라미터와 ‘(‘, ‘)’ 사이에는 공백이 없다.
-
{ 과 } 는 단독으로 한 줄을 사용한다.
-
모든 파라미터는 이름이 있어야 하며 선언과 구현의 파라미터명이 동일해야 한다.
ReturnType LongClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
Type par_name3)
{
DoSomething(); // 1 tab indent
...
}
-
리턴타입, 함수명, 파라미터를 한 줄로 쓰기에 길면 파라미터 일부를 다음 줄에 적는다.
-
새로운 줄의 파라미터는 첫 번째 파라미터를 기준으로 정렬한다.
2.1.4. 함수 호출
bool retval = DoSomething(argument1, argument2, argument3);
if (...)
{
bool retval = DoSomethingThatRequiresALongFunctionName(
very_long_argument1, // 1 tab indent
argument2,
argument3,
argument4);
}
함수명과 파라미터를 한 줄로 쓰기에 길면 파라미터를 일부를 다음 줄에 적는다. 들여쓰기와 정렬방식은 ‘함수 선언과 정의’의 내용과 같다.
2.1.5. 조건문
if (condition) // no spaces inside parentheses
{
... // 1 tab indent.
}
else if (...)
{
...
}
else
{
...
}
if (condition)
{
DoSomething(); // 1 tab indent.
}
-
괄호안에 공백이 없어야 하며 else 키워드는 새 줄에 적는다.
-
if 와 괄호사이에 공백1개가 있어야 한다.
-
{ 과 } 는 단독으로 한 줄을 사용한다.
if (condition)
{
DoSomething(); // 1 tab indent.
}
if (x == kFoo)
{
return new Foo();
}
2.1.6. 루프와 Switch문
switch (var)
{
case 0:
{
...
break;
}
case 1:
{
...
break;
}
default:
{
assert(false);
}
}
switch (var)
{
case 0:
...
break;
case 1:
...
break;
default:
assert(false);
}
-
switch문의 case에 {, }를 사용하는 경우 {, }는 단독으로 한 줄을 사용한다.
while (condition)
{
}
for (int i = 0; i < kSomeNumber; ++i)
{
}
while (condition)
{
continue;
}
-
조건문만 있고 몸체가 없는 loop인 경우 { } 또는 continue 문을 사용한다.
2.1.7. 포인터와 참조 표현식
x = *p;
p = &x;
x = r.y;
x = r->y;
// These are fine, space preceding.
char *c;
const string &str;
화살표(→)와 점(.) 앞뒤에 공백을 넣지 않는다. 포인터연산자(*, &) 다음에도 공백을 넣지 않는다. 선언문의 *, & 다음에도 공백을 넣지 않는다.
2.1.8. 표현식
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another && last_one)
{
...
}
조건문의 boolean 표현식(논리식)이 길어서 한 줄로 쓰기에 길면 위와 같은 형식으로 줄을 바꾼다. 가독성을 높이기 위해 ‘(‘, ‘)’를 적절히 사용한다.
2.1.9. 리턴 값
return result;
return (some_long_condition &&
another_condition);
리턴값(return expr; 의 expr)을 ‘(‘, ‘)’로 묶지 않는다. 단, 가독성이 향상되는 경우에는 괄호를 쓸 수 있다.
2.1.10. 변수와 배열 초기화
int x = 3;
int x(3);
string name("Some Name");
string name = "Some Name";
변수 초기화를 위해 = 와 ( )를 사용할 수 있다.
2.1.11. 프리프로세서 지시어
// Good - directives at beginning of line
if (lopsided_score)
{
#if DISASTER_PENDING // Correct -- Starts at beginning of line
DropEverything();
# if NOTIFY // OK but not required -- Spaces after #
NotifyClient();
# endif
#endif
BackToNormal();
}
프리프로세서 지시어는 들여쓰기를 하지 않고 항상 첫 번째 열에서 시작한다.
2.1.12. 생성자 초기화 리스트
// When it all fits on one line:
MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1)
{
}
// When it requires multiple lines, indent 2 tab, putting the colon on
// the first initializer line:
MyClass::MyClass(int var)
: some_var_(var), // 2 tab indent
some_other_var_(var + 1) // 2 tab indent + 2 space
{
...
DoSomething();
...
}
생성자의 초기화 리스트는 위와 같은 포맷으로 한 줄에 쓰거나 여러 줄에 나누어 쓴다.
2.1.13. 네임스페이스 포맷팅
namespace foo {
namespace bar {
void foo() // Correct. No extra indentation within namespace
{
...
}
} // namespace bar
} // namespace foo
네임스페이스는 시작 괄호‘{‘를 namespace 키워드와 같은 줄에 쓰며, 들여쓰기 수준을 증가 시키지 않는다.
2.1.14. 가로 여백 (Horizental Whitespace)
int i = 0; // Semicolons usually have no space before them.
int x[] = {0};
// Spaces around the colon in inheritance and initializer lists.
class Foo : public Bar
{
public:
// For inline function implementations, put spaces between the braces
// and the implementation itself.
Foo(int b) : Bar(), baz_(b) {} // No spaces inside empty braces.
void Reset() { baz_ = 0; } // Spaces separating braces from implementation.
}
}
-
일반적으로 세미콜론 ‘;’ 앞에는 공백을 두지 않는다.
-
배열 초기화인 경우 초기화 데이터와 ‘{‘, ‘}’ 사이에 공백을 넣지 않는다.
-
함수 정의를 1줄로 작성 할 때는 구현코드와 ‘{‘, ‘}’사이에 공백을 넣는다.
-
빈 괄호 ( ), { } 안에도 공백을 넣지 않는다.
while (test) // There is usually no space inside parentheses.
switch (i)
for (int i = 0; i < 5; ++i)
for (; i < 5; ++i) // For loops always have a space after the
// semicolon.
switch (i)
case 1: // No space before colon in a switch case.
...
case 2: break; // Use a space after a colon if there's code after it.
-
while, switch, for의 괄호 (, ) 는 안쪽에 공백을 넣지 않고 붙여 쓴다.
-
for문의 세미콜론(;) 다음에는 항상 공백을 넣는다.
-
case문의 콜론(:) 앞에는 공백을 두지 않는다. 콜론(:) 다음에는 공백을 넣는다.
x = 0; // Assignment operators always have spaces around
// them.
x = -5; // No spaces separating unary operators and their
++x; // arguments.
if (x && !y)
...
v = w * x + y / z; // Binary operators usually have spaces around them,
v = w*x + y/z; // but it's okay to remove spaces around factors.
v = w * (x + z); // Parentheses should have no spaces inside them.
-
대입 연산자 (=) 앞뒤로 공백을 넣는다.
-
일항 연산자(unary operator; -, ++, !)는 공백없이 붙여 쓴다.
-
이항 연산자는 앞뒤로 공백을 넣는다. 단, *,/ 는 공백없이 쓸 수 있다.
-
연산자 괄호 (, ) 도 안쪽에 공백을 넣지 않고 붙여 쓴다.
vector<string> x; // No spaces inside the angle
y = static_cast<char*>(x); // brackets (< and >), before
// <, or between >( in a cast.
-
템플릿 괄호 <, > 안쪽에 공백을 넣지 않고 붙여 쓴다.
-
템플릿 괄호 <, > 전후로도 붙여 쓴다.
2.2. 주석 (Comments)
2.2.2. 파일 주석
//=============================================================================
//
// Copyright (c) 2010-2012, JS Solution All Rights Reserved.
//
// No part of this program may be copied, reproduced, stored in a
// retrieval system, or transmitted in any form or by any means
// - electronic, mechanical, photocopying, recording, or otherwise -
// without written permission of the licensee, JS Solution
//
//==============================================================================
///
/// @file hdsr.cpp
/// @brief hdsr process
///
/// 실행중인 서버가 Master인 경우 HDSR버퍼에 저장된 포인트 값을 읽어서 이력데이터파일로
/// 저장하고 Hdsr Sync를 통해 Slave에게 포인트 값을 보낸다. 실행중인 서버가 Slave인 경우
/// Hdsr Sync를 통해 Master로부터 포인트 값을 전달받아 이력데이터파일로 저장한다.
///
/// @author ws.woo
/// @date 2011-10-05
///
-
모든 .h, .cpp파일은 저작권 안내(저작권 표시, 라이선스문구, 작성자)로 시작 한다. 그 다음에 파일의 내용을 기술하는 주석을 쓴다.
-
일반적으로 .h 에는 클래스가 무엇인지, 어떻게 사용하는지에 대해 기술하는 주석을 쓰고, .cpp에는 상세 구현에 대한 자세한 정보를 쓴다.
-
.h와 .cpp에 같은 내용의 주석을 중복해서 쓰지 않는다.
2.2.3. 클래스 주석
///
/// @brief 여기에 클래스에 대한 설명을 적는다.
///
/// 클래스에 대한 자세한 설명
///
/// 사용 예:
/// @verbatim
/// GargantuanTableIterator* iter = table->NewIterator();
/// for (iter->Seek("foo"); !iter->done(); iter->Next())
/// {
/// process(iter->key(), iter->value());
/// }
/// delete iter;
/// @endverbatim
///
class GargantuanTableIterator
{
...
};
모든 클래스 정의에는 그 클래스가 무엇인지 어떻게 사용하는지를 기술하는 주석을 써야한다.
2.2.4. 함수 주석
class MyClass
{
public:
///
/// @brief 함수에 대한 간단한 설명
/// 함수에 대한 간단한 설명 좀 더
///
/// 함수에 대한 자세한 설명
/// 기타 자세한 설명
///
/// @param [in] param1
/// param1에 대한 설명
/// @param [in,out] param2
/// param2에 대한 설명
/// @param [out] param3
/// param3에 대한 설명
///
/// @return 출력값에 대한 설명
/// @retval 1 성공
/// @retval 0 실패
int Foo(int value);
}
-
모든 함수 선언 앞에는 함수가 무엇이고 어떻게 사용하는지, 입력과 출력을 기술해야 한다.
//
// @brief
// A brief description of the function.
// More brief description.
//
// Details follow here.
// Maybe an example or something.
//
// @param value
// Parameter description for parameter named 'value'
//
// @return description
int MyClass::Foo(int value)
{
return value + 2;
}
-
함수 정의부에는 함수가 어떻게 동작하는지(알고리즘)를 기술한다.
2.2.5. 변수 주석
private:
/// Keeps track of the total number of entries in the table.
/// Used to ensure we do not go over the limit. -1 means
/// that we don't yet know how many entries the table has.
int num_total_entries_;
/// The total number of tests cases that we run through in this regression test.
const int kNumTestCases = 6;
const int kNumTestCases = 6; ///< description for kNumTestCases
-
일반적으로 변수 이름만으로도 변수가 무엇을 위해 사용되는지 충분히 알 수 있어야 한다.
-
변수의 설명은 변수 설명의 앞에 넣는다. 짧은 경우 오른쪽에 둘 수 있다.
-
무엇을 나타내는 변수인지, 무엇을 위해 사용되는지를 기술한다.
2.2.6. 구현 주석
// Divide result by two, taking into account that x
// contains the carry from the add.
for (int i = 0; i < result->size(); i++)
{
x = (x << 8) + (*result)[i];
(*result)[i] = x >> 1;
x &= 1;
}
bool success = CalculateSomething(interesting_value,
10, // Default base value.
false, // Not the first time we're calling this.
NULL); // No callback.
const int kDefaultBaseValue = 10;
const bool kFirstTimeCalling = false;
Callback *null_callback = NULL;
bool success = CalculateSomething(interesting_value,
kDefaultBaseValue,
kFirstTimeCalling,
null_callback);
-
트릭이 있거나 복잡한 코드 블록이 있으면 앞에 주석을 달아야 한다.
-
라인 설명을 위해 라인 끝에 주석을 쓸 때는 2공백을 띄우거나 앞, 뒤의 라인 주석과 정렬한다.
-
NULL, true/false, 1, 2, 3등을 함수의 파라미터로 쓸 때는 주석을 달거나 적절한 변수 이름을 사용한다.
2.3. Clang Format
들여쓰기나 괄호 위치, 줄바꿈, 빈칸 삽입등은 clang format 도구를 사용하여 자동화 할 수 있다. 다음은 clang-format 프로그램의 입력파일(.clang-format)이다.
---
Language: Cpp
# BasedOnStyle: Google
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '"pch.h"'
Priority: -1
- Regex: '"stdafx.h"'
Priority: -1
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: BEGIN_MESSAGE_MAP
MacroBlockEnd: END_MESSAGE_MAP
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 4
UseTab: Always
...
3. 기초 코딩 컨벤션
3.1. 네이밍 (Naming)
이름만으로 별도 검색없이 타입, 변수, 함수, 상수, 매크로 여부를 알 수 있도록 일관성있게 적용한다.
3.1.1. 일반 규칙
int num_errors; // Good.
int num_completed_connections; // Good.
// Good
// These show proper names with no abbreviations.
int num_dns_connections; // Most people know what "DNS" stands for.
int price_count_reader; // OK, price count. Makes sense.
OpenFile();
set_num_errors()
int n; // Bad - meaningless.
int nerr; // Bad - ambiguous abbreviation.
int n_comp_conns; // Bad - ambiguous abbreviation.
// Bad!
// Abbreviations can be confusing or ambiguous outside a small group.
int wgc_connections; // Only your group knows what this stands for.
int pc_reader; // Lots of things can be abbreviated "pc".
함수명, 변수명, 파일명은 서술적으로 쓰고 약어를 피한다. 타입, 변수는 명사형이어햐 하고, 함수는 동사여야 한다.
3.1.2. 파일 이름
my_useful_class.cpp
myusefulclass.cpp
myusefulclass_test.cpp
FooBar.h
FooBar.cpp
UrlTable.h
UrlTable.cpp
UrlTableFoo.inc
-
파일명은 소문자와 밑줄문자( _ )로 만든다. 또는 대문자로 시작하며 구성하는 단어마다 시작위치에만 대문자를 넣는다.
-
C++파일은 .cpp로 끝나고 헤더파일은 .h로 끝나야 한다. 예를들어 FooBar 클래스를 정의하는 경우 foo_bar.h, foo_bar.cpp를 만든다.
-
inline함수는 .h파일에 넣는다. inline함수들의 코드가 아주 크면 .inc로 끝나는 제3의 파일을 만든다.
3.1.3. 타입 이름
// classes and structs
class UrlTable
{
class UrlTableTester
{
struct UrlTableProperties
{
// typedefs
typedef hash_map<UrlTableProperties *, string> PropertiesMap;
// enums
enum UrlTableErrors
{
-
클래스 이름, 구조체 이름, typedef 이름, enum 이름 모두 동일한 규칙을 적용한다.
-
타입 이름은 대문자로 시작하며 구성하는 단어마다 시작위치에만 대문자를 넣는다.
3.1.4. 변수 이름
string table_name; // OK - uses underscore.
string tablename; // OK - all lowercase.
class Test
{
private:
string table_name_; // OK - underscore at end.
string tablename_; // OK.
}
struct UrlTableProperties
{
string name;
int num_entries;
}
-
일반적으로 변수 이름은 소문자와 밑줄문자( _ )만으로 만든다. 대문자를 섞어 쓰지 않는다.
-
클래스의 멤버 변수는 밑줄문자( _ )로 끝난다.
-
구조체의 멤버(필드)명은 클래스의 멤버 변수와 달리 밑줄문자( _ )로 끝나지 않는다.
-
전역 변수는 가급적 정의하지 않는다. 정의할 경우 전역 변수 이름은 g_ 로 시작한다.
3.1.5. 상수 이름
const int kDaysInAWeek = 7;
상수 이름은 k 로 시작하며 대소문자를 혼용한다. 상수는 종류(지역, 전역, 클래스)에 상관없이 일반 변수 이름과 다른 명명 규칙을 적용한다.
3.1.6. 함수 이름
AddTableEntry()
DeleteUrl()
OpenFileOrDie()
class MyClass
{
public:
...
int num_entries() const { return num_entries_; }
void set_num_entries(int num_entries) { num_entries_ = num_entries; }
private:
int num_entries_;
};
-
일반 함수 이름은 대소문자를 혼용한다.
-
접근자(accessor), 수정자(set, mutator)는 변수 이름과 일치시킨다.
3.2. 헤더파일 (Header Files)
main() 함수만 있는 작은 cpp파일처럼 특별한 경우를 제외하면, 일반적으로 cpp파일마다 연관된 .h파일이 있어야 한다. 헤더파일은 자체 완비(self-contained) 되어 있어야 한다.
3.2.1. define 가드
// Copyright 문
#ifndef FILE_IO_H_
#define FILE_IO_H_
// src/common_code/file_system/file_io.h 파일인 경우
#endif // FILE_IO_H_
// 또는
#ifndef COMMON_CODE_FILE_SYSTEM_FILE_IO_H_
#define COMMON_CODE_FILE_SYSTEM_FILE_IO_H_
// src/common_code/file_system/file_io.h 파일인 경우
#endif // COMMON_CODE_FILE_SYSTEM_FILE_IO_H_
-
모든 헤더파일은 중복 include를 방지하기 위해 #define 가드를 넣는다.
-
정의기호는 파일명(대문자)_ 형식으로 한다. 동일한 파일 이름이 생길 가능성이 높을 때는 디렉토리명_파일명(대문자)_ 형식으로 한다.
3.2.6. 인클루드 이름과 순서
#include "foo/public/fooserver.h" // Preferred location.
#include <sys/types.h>
#include <unistd.h>
#include <hash_map>
#include <vector>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/shared_ptr.hpp>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/public/bar.h"
-
헤더파일 include순서는 C library, C++ library, 3rd party library, Project library 순으로 하고 라이브러리별(C, C++, 3rd party, Project)로 알파벳 순서로 include한다.
-
프로젝트 헤더파일은 소스 디렉토리 기준으로 (.. : 부모디렉토리)를 사용하지 않는 상대 경로로 표시한다.
-
헤더파일을 구현하는 cpp에서 include하는 경우 제일 먼저 include한다.
4. 고급 코딩 컨벤션
4.1. 클래스 (Classes)
4.1.1. 멤버 선언 순서
class MyClass : public OtherClass
{
public: // Note the no indent!
MyClass(); // Regular 1 tab indent.
explicit MyClass(int var);
~MyClass() {}
void SomeFunction();
void SomeFunctionThatDoesNothing() {}
void set_some_var(int var) { some_var_ = var; }
int some_var() const { return some_var_; }
private:
bool SomeInternalFunction();
int some_var_;
int some_other_var_;
DISALLOW_COPY_AND_ASSIGN(MyClass);
};
-
base 클래스명은 서브클래스명과 같은 라인에 적는다.
-
public:, protected:, private: 키워드는 들여쓰기 하지 않는다.
-
public, protected, private 순서로 구역을 나눈다. 빈 줄로 구역을 구분한다.
-
각 구역마다 method, data member 순으로 선언한다. 자세한 순서는 다음과 같다.
-
Typedef, Enums, Constants (static const)
-
생성자, 소멸자
-
method, data member (except static const)
-
-
friend 선언과 DISALLOW_COPY_AND_ASSIGN 매크로는 private 구역에 있어야 한다.
4.1.2. 생성자에서의 작업
생성자에서는 멤버 변수의 초기값만 세팅하고 다른 복잡한 초기화 작업은 Initialize() 메소드에서 한다. (생성자에서 발생한 에러는 체크하기가 어려우므로 에러가 발생할 가능성이 있는 모든 작업은 Initialize() 메소드에서 한다.)
4.1.3. 디폴트 생성자
멤버변수가 있는 클래스를 정의하면서 어떤 생성자도 정의하지 않았다면 디폴트 생성자를 정의해야 한다. 상속 클래스에서 멤버변수 추가가 없는 경우에는 필요없다.
4.1.5. 복사 생성자
-
복사 생성자와 대입 연산자는 필요할 때만 정의하여 사용하고, 그렇지 않으면 DISALLOW_COPY_AND_ASSIGN를 써서 사용할 수 없게 만든다. (묵시적인 객체복사는 버그가 발생하기 쉽고 성능과 가독성에 안좋은 영향을 주기 때문이다.)
-
복사 생성자가 필요한 경우에도 가능하면 CopyFrom() 메소드를 정의하여 복사 생성자를 대신한다.
4.1.6. 구조체(structs)와 클래스(classes)
-
구조체는 자료를 담는 수동적인 객체를 위해서만 사용하고, 그 밖의 것은 클래스를 사용한다.
-
구조체에는 상수도 정의할 수 있으나 필드(멤버)를 읽고 쓰는 기능외에는 없다.
-
필드를 읽고 쓸 때는 메소드를 사용하지 않고 직접 필드에 접근한다.
-
구조체의 메소드는 필드를 setup 할 때만 사용한다. 예를들면, 생성자, 소멸자, Initialize(), Reset(), Validate() 등이다.
-
기능성이 더 필요한 경우에는 클래스를 사용한다.
-
STL과의 일관성을 위해 functors 나 traits에 대해 클래스 대신 구조체를 사용할 수 있다.
-
‘변수 이름’에서 정했듯이 구조체와 클래스 멤버 변수의 명명규칙은 다르다.
4.1.9. 인터페이스
클래스 이름이 Interface로 끝나는 경우 아래 조건을 모두 만족해야 한다.
-
public pure virtual 메소드와 static 메소드만 있다.
-
static 아닌 데이터 멤버가 없다.
-
생성자가 없어도 된다. 생성자가 있다면 인자 없이 protected 영역에 정의되어 있어야 한다.
-
서브클래스인 경우 부모클래스의 이름이 Interface로 끝난다.
4.1.10. 연산자 오버로딩
클래스에 대한 +, /, =, == 같은 연산자 오버로딩은 사용하지 않는다.
CopyFrom(), Equals() 같은 일반 메소드를 =, == 대신 정의하여 사용한다.
4.1.11. 접근 제어
class SampleClass
{
public:
static const int kMaxRetry = 3;
inline int foo() { return foo_; } // accessor 함수
inline void set_foo(int foo) { foo_ = foo; } // mutator 함수
private:
int foo_; // Sample
};
-
데이터 멤버는 private으로 선언하고 필요한 경우 accessor 함수를 제공한다.
-
변수명, accessor 함수, mutator함수는 foo_, foo(), set_foo() 형식를 가진다.
-
static const 데이터 멤버는 private이 아닐 수 있다.
4.2. 범위 (Scoping)
4.2.1. 네임스페이스
// This is in a .cpp file.
namespace {
// The content of a namespace is not indented
enum { kUnused, kEOF, kError }; // Commonly used tokens.
bool AtEof() { return pos_ == kEOF; } // Uses our namespace's EOF.
} // namespace
-
이름 충돌을 피하기위해 이름없는 네임스페이스를 cpp파일에 사용할 수 있다.
-
네임스페이스에 이름을 붙일 경우 프로젝트명과 파일경로에 기초하여 정한다.
#include "a.h"
DEFINE_bool(someflag, false, "dummy flag");
class C; // Forward declaration of class C in the global namespace.
namespace a { class A; } // Forward declaration of a::A.
namespace b {
// Definition of functions is within scope of the namespace.
void MyClass::Foo()
{
...
}
} // namespace b
-
네임스페이스 영역은 들여쓰기를 하지 않는다.
-
네임스페이스는 include문, 타 네임스페이스의 클래스 포워드선언 이후 전체 소스를 감싼다.
// Forbidden -- This pollutes the namespace.
using namespace foo;
-
헤더 파일내에 using 지시어(using-directive)는 사용하지 않는다.
// OK in .cc files.
// Must be in a function, method or class in .h files.
using ::foo::bar;
// Shorten access to some commonly used names in .cc files.
namespace fbz = ::foo::bar::baz;
// Shorten access to some commonly used names (in a .h file).
namespace librarian {
// The following alias is available to all files including
// this header (in namespace librarian):
// alias names should therefore be chosen consistently
// within a project.
namespace pd_s = ::pipeline_diagnostics::sidetable;
inline void my_inline_function()
{
// namespace alias local to a function (or method).
namespace fbz = ::foo::bar::baz;
...
}
} // namespace librarian
-
using 선언문(using-declaration)과 네임스페이스 alias를 사용한다.
4.2.2. 비멤버 함수, 정적 멤버함수, 전역함수
-
전역함수를 정의하는 대신 네임스페이스안에 비멤버함수를 정의하거나 정적 멤버함수를 정의한다. (클래스의 멤버함수가 아닌 함수를 모두 비멤버함수라고 한다.)
-
비멤버함수는 외부변수에 의존하지 않고 한 네임스페이스안에 있어야 한다.
-
static 데이터를 공유하지 않는 정적 멤버함수만 모아놓은 클래스를 만드는 대신 네임스페이스를 사용한다.
-
동일한 .cpp파일 내에서만 필요한 비멤버함수를 정의 할 때는 이름없는 네임스페이스 또는 static 키워드를 써서 범위를 한정시킨다.