6.3 KiB
6.3 KiB
C++ コーディング規約
1. 一般的なガイドライン
- モダン C++(C++17 以上) を前提とする。
- 生ポインタより、まず
std::unique_ptr/std::shared_ptr/std::vectorなど RAII を優先する。 - 例外を使う場合は、例外 or エラーコードのどちらかに統一する。
new/deleteは基本的に直接使わず、スマートポインタやコンテナを使う。- ヘッダ/ソース分割は
*.h/*.cpp(または*.hpp/*.cpp)で行う。 - 自動整形には
clang-formatを利用する。
2. コメント/ドキュメント
2.1 ファイルコメント
// user_service.h - ユーザー関連のユースケースを提供するクラス群
#pragma once
// ...
2.2 クラスコメント(Doxygen 推奨)
/**
* @brief ユーザーを管理するサービスクラス。
*
* ユーザーの作成、取得、削除などの機能を提供する。
*/
class UserService {
public:
/// コンストラクタ
explicit UserService(UserRepository& repo);
/// ユーザーを作成する
User createUser(const std::string& name, int age);
private:
UserRepository& repo_;
};
2.3 メソッドコメント
/**
* @brief ユーザーをIDで取得する。
*
* @throws std::runtime_error ユーザーが存在しない場合。
*/
User UserService::getUserById(int id);
3. 命名規則
-
クラス名/構造体名:
CamelCaseclass UserService; struct HttpRequest; -
メソッド・関数名:
camelCaseまたはsnake_case(プロジェクト内で統一)void addUser(const User& user); int find_user(const std::string& name); // プロジェクト方針次第 -
メンバ変数:
- プレフィックスやサフィックスを付けるスタイルが多い
例:
snake_case_/m_name/_nameなど
class User { public: explicit User(std::string name, int age) : name_(std::move(name)), age_(age) {} private: std::string name_; int age_; }; - プレフィックスやサフィックスを付けるスタイルが多い
例:
-
定数:
- グローバル定数には
kプレフィックスを使う場合も多い。
constexpr int kMaxUserCount = 1000; - グローバル定数には
-
名前空間: すべて小文字でプロジェクト名などを使う。
namespace myapp::user { // ... }
4. インクルードガイドライン
-
ヘッダでのインクルードは 最小限 にする。前方宣言で済むなら前方宣言を使う。
// user_service.h #pragma once #include <string> namespace myapp { class UserRepository; // 前方宣言 class UserService { public: explicit UserService(UserRepository& repo); // ... private: UserRepository& repo_; }; } // namespace myapp -
ソースファイルでは対応するヘッダをまずインクルードし、その後に標準/外部ライブラリ。
// user_service.cpp #include "user_service.h" #include "user_repository.h" #include <stdexcept> -
インクルード順は以下を推奨:
- 自分のヘッダ
- 同じモジュールのヘッダ
- 標準ライブラリ
- サードパーティヘッダ
5. エラーハンドリング
5.1 例外を使う場合
-
ロジック上の失敗には
throwで例外を投げ、呼び出し側でtry/catch。User UserService::getUserById(int id) { auto user = repo_.findById(id); if (!user) { throw std::runtime_error("User not found"); } return *user; } -
例外の種類は、
std::runtime_error/std::invalid_argumentなどを使い分ける。 -
ライブラリ側では「例外を投げる API」と「エラーコードを返す API」を明確に分ける。
5.2 例外を使わない場合
-
戻り値に
std::optional<T>やexpectedパターン(独自実装)を使う。std::optional<User> UserService::tryGetUserById(int id) { return repo_.findById(id); }
6. 複数ファイル間の分割(ヘッダ / ソース)
6.1 例: User と UserService
include/
user.h
user_service.h
src/
user.cpp
user_service.cpp
main.cpp
user.h
#pragma once
#include <string>
namespace myapp {
class User {
public:
User(int id, std::string name);
int id() const;
const std::string& name() const;
private:
int id_;
std::string name_;
};
} // namespace myapp
user.cpp
#include "user.h"
namespace myapp {
User::User(int id, std::string name)
: id_(id), name_(std::move(name))
{
}
int User::id() const
{
return id_;
}
const std::string& User::name() const
{
return name_;
}
} // namespace myapp
user_service.h
#pragma once
#include <memory>
#include "user.h"
namespace myapp {
class UserRepository;
class UserService {
public:
explicit UserService(UserRepository& repo);
User createUser(const std::string& name, int age);
private:
UserRepository& repo_;
};
} // namespace myapp
user_service.cpp
#include "user_service.h"
#include "user_repository.h"
namespace myapp {
UserService::UserService(UserRepository& repo)
: repo_(repo)
{
}
User UserService::createUser(const std::string& name, int age)
{
// 仮実装: ID採番して保存するなど
int newId = repo_.nextId();
User user(newId, name);
repo_.save(user);
return user;
}
} // namespace myapp
7. clang-format / clang-tidy / VSCode 設定
7.1 .clang-format(C と共有してもOK)
BasedOnStyle: LLVM
IndentWidth: 4
UseTab: Never
ColumnLimit: 100
AllowShortIfStatementsOnASingleLine: false
BreakBeforeBraces: Attach
NamespaceIndentation: All
7.2 VSCode 設定例
{
"[cpp]": {
"editor.defaultFormatter": "ms-vscode.cpptools",
"editor.formatOnSave": true
},
"C_Cpp.clang_format_style": "file",
// clang-tidy を使う場合
"C_Cpp.codeAnalysis.clangTidy.enabled": true,
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true
}