Coding Rule
* header order
dir2/foo2.h
.- C system files.
- C++ system files.
- Other libraries'
.h
files. - Your project's
.h
files.
* inline function 권장
: but 10 줄 이내만!
* namespace
: 가능하면 namespace 사용
* 소스 내부에만 사용하는 변수
: class 에 선언하지 말고 가능하면 소스내에 unnamed namespace나 static 변수로 선언
* 로컬변수는 사용하는 곳 바로 위에 선언해라. 그래야 읽기 쉬움.
* 루프의 변수는 루프내에 선언하고, object일 경우는 밖에 할 것.
obj의 경우 loop를 나올 때마다 constructor/destructor가 계속 불림
* 전체 global 변수는 허용 안함
BUT, constexpr만 허용
* static 변수는 가능하면 built-in type만 (std::string 대신에 const char[] 사용!!)
* class
If you do not want to support copy/move operations on your type, explicitly disable them using = delete
in the public:
section:
// MyClass is neither copyable nor movable. MyClass(const MyClass&) = delete; MyClass& operator=(const MyClass&) = delete;
* Operator overloading
Don't go out of your way to avoid defining operator overloads. For example, prefer to define ==
, =
, and <<
, rather than Equals()
, CopyFrom()
, and PrintTo()
. Conversely, don't define operator overloads just because other libraries expect them. For example, if your type doesn't have a natural ordering, but you want to store it in a std::set
, use a custom comparator rather than overloading <
.
Do not overload &&
, ||
, ,
(comma), or unary &
. Do not overload operator""
, i.e. do not introduce user-defined literals.
* Declaration Order
Group similar declarations together, placing public parts earlier.
A class definition should usually start with a public:
section, followed by protected:
, then private:
. Omit sections that would be empty.
Within each section, generally prefer grouping similar kinds of declarations together, and generally prefer the following order: types (including typedef
, using
, and nested structs and classes), constants, factory functions, constructors, assignment operators, destructor, all other methods, data members.
Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be defined inline. See Inline Functions for more details.
* parameter order : in, out
input parameters are usually values or const
references,
* Write short functions : function은 40줄을 넘지 않게!!
* Reference arguments
Within function parameter lists all references must be const
:
void Foo(const string &in, string *out);
In fact it is a very strong convention in Google code that input arguments are values or const
references while output arguments are pointers. Input parameters may be const
pointers, but we never allow non-const
reference parameters except when required by convention, e.g., swap()
.
Braced Initializer List
You may use braced initializer lists.
In C++03, aggregate types (arrays and structs with no constructor) could be initialized with braced initializer lists.
struct Point { int x; int y; }; Point p = {1, 2};
In C++11, this syntax was generalized, and any object type can now be created with a braced initializer list, known as a braced-init-list in the C++ grammar. Here are a few examples of its use.
// Vector takes a braced-init-list of elements. std::vector<string> v{"foo", "bar"}; // Basically the same, ignoring some small technicalities. // You may choose to use either form. std::vector<string> v = {"foo", "bar"}; // Usable with 'new' expressions. auto p = new vector<string>{"foo", "bar"}; // A map can take a list of pairs. Nested braced-init-lists work. std::map<int, string> m = {{1, "one"}, {2, "2"}}; // A braced-init-list can be implicitly converted to a return type. std::vector<int> test_function() { return {1, 2, 3}; } // Iterate over a braced-init-list. for (int i : {-1, -2, -3}) {} // Call a function using a braced-init-list. void TestFunction2(std::vector<int> v) {} TestFunction2({1, 2, 3});
ambda expressions
Use lambda expressions where appropriate. Prefer explicit captures when the lambda will escape the current scope.
Lambda expressions are a concise way of creating anonymous function objects. They're often useful when passing functions as arguments. For example:
std::sort(v.begin(), v.end(), [](int x, int y) { return Weight(x) < Weight(y); });
They further allow capturing variables from the enclosing scope either explicitly by name, or implicitly using a default capture. Explicit captures require each variable to be listed, as either a value or reference capture:
int weight = 3; int sum = 0; // Captures `weight` by value and `sum` by reference. std::for_each(v.begin(), v.end(), [weight, &sum](int x) { sum += weight * x; });
* cpplint 파일
https://google.github.io/styleguide/cppguide.html#cpplint
Use of const
Use const
whenever it makes sense. With C++11, constexpr
is a better choice for some uses of const.
Declared variables and parameters can be preceded by the keyword const
to indicate the variables are not changed (e.g., const int foo
). Class functions can have the const
qualifier to indicate the function does not change the state of the class member variables (e.g., class Foo { int Bar(char c) const; };
).
Easier for people to understand how variables are being used. Allows the compiler to do better type checking, and, conceivably, generate better code. Helps people convince themselves of program correctness because they know the functions they call are limited in how they can modify your variables. Helps people know what functions are safe to use without locks in multi-threaded programs.
const
is viral: if you pass a const
variable to a function, that function must have const
in its prototype (or the variable will need a const_cast
). This can be a particular problem when calling library functions.
const
variables, data members, methods and arguments add a level of compile-time type checking; it is better to detect errors as soon as possible. Therefore we strongly recommend that you use const
whenever it makes sense to do so:
- If a function guarantees that it will not modify an argument passed by reference or by pointer, the corresponding function parameter should be a reference-to-const (
const T&
) or pointer-to-const (const T*
), respectively. - Declare methods to be
const
whenever possible. Accessors should almost always beconst
. Other methods should be const if they do not modify any data members, do not call any non-const
methods, and do not return a non-const
pointer or non-const
reference to a data member. - Consider making data members
const
whenever they do not need to be modified after construction.
The mutable
keyword is allowed but is unsafe when used with threads, so thread safety should be carefully considered first.
reincrement and Predecrement
Use prefix form (++i
) of the increment and decrement operators with iterators and other template objects.
Ownership and Smart Pointers
Prefer to have single, fixed owners for dynamically allocated objects. Prefer to transfer ownership with smart pointers.
"Ownership" is a bookkeeping technique for managing dynamically allocated memory (and other resources). The owner of a dynamically allocated object is an object or function that is responsible for ensuring that it is deleted when no longer needed. Ownership can sometimes be shared, in which case the last owner is typically responsible for deleting it. Even when ownership is not shared, it can be transferred from one piece of code to another.
"Smart" pointers are classes that act like pointers, e.g. by overloading the *
and ->
operators. Some smart pointer types can be used to automate ownership bookkeeping, to ensure these responsibilities are met.std::unique_ptr
is a smart pointer type introduced in C++11, which expresses exclusive ownership of a dynamically allocated object; the object is deleted when the std::unique_ptr
goes out of scope. It cannot be copied, but can be moved to represent ownership transfer. std::shared_ptr
is a smart pointer type that expresses shared ownership of a dynamically allocated object. std::shared_ptr
s can be copied; ownership of the object is shared among all copies, and the object is deleted when the last std::shared_ptr
is destroyed.
If dynamic allocation is necessary, prefer to keep ownership with the code that allocated it. If other code needs access to the object, consider passing it a copy, or passing a pointer or reference without transferring ownership. Prefer to use std::unique_ptr
to make ownership transfer explicit. For example:
std::unique_ptr<Foo> FooFactory(); void FooConsumer(std::unique_ptr<Foo> ptr);
Do not design your code to use shared ownership without a very good reason. One such reason is to avoid expensive copy operations, but you should only do this if the performance benefits are significant, and the underlying object is immutable (i.e. std::shared_ptr<const Foo>
). If you do use shared ownership, prefer to use std::shared_ptr
.
Never use std::auto_ptr
. Instead, use std::unique_ptr
.
Rvalue References
Use rvalue references only to define move constructors and move assignment operators, or for perfect forwarding.
Rvalue references are a type of reference that can only bind to temporary objects. The syntax is similar to traditional reference syntax. For example, void f(string&& s);
declares a function whose argument is an rvalue reference to a string.
- Defining a move constructor (a constructor taking an rvalue reference to the class type) makes it possible to move a value instead of copying it. If
v1
is astd::vector<string>
, for example, thenauto v2(std::move(v1))
will probably just result in some simple pointer manipulation instead of copying a large amount of data. In some cases this can result in a major performance improvement. - Rvalue references make it possible to write a generic function wrapper that forwards its arguments to another function, and works whether or not its arguments are temporary objects. (This is sometimes called "perfect forwarding".)
- Rvalue references make it possible to implement types that are movable but not copyable, which can be useful for types that have no sensible definition of copying but where you might still want to pass them as function arguments, put them in containers, etc.
std::move
is necessary to make effective use of some standard-library types, such asstd::unique_ptr
.
- Rvalue references are a relatively new feature (introduced as part of C++11), and not yet widely understood. Rules like reference collapsing, and automatic synthesis of move constructors, are complicated.
Use rvalue references only to define move constructors and move assignment operators (as described in Copyable and Movable Types) and, in conjunction with std::forward
, to support perfect forwarding. You may use std::move
to express moving a value from one object to another rather than copying it.