「C++」の版間の差分

提供:senooken JP Wiki
(New)
 
(カテゴリー追加。)
 
(同じ利用者による、間の13版が非表示)
1行目: 1行目:
== About ==
* [https://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms More C++ Idioms - Wikibooks]
== Core ==
=== 前方宣言 ===
出典: [https://dormolin.livedoor.blog/archives/52220096.html 【C++】ヘッダー内に他のヘッダーをincludeする量を減らそう : あんちょこ]。
C/C++がなぜヘッダーとソースファイルを分けているか。ファイル単位の依存関係を減らして、ビルドしやすくするため。
ヘッダーしか存在しない場合、どれか1箇所でも変更すると、全部ビルドが必要になる。
ソースファイルはobjectファイルにコンパイルして、最後にリンクすればいい。
同じ理由で、ソースファイルには好きなだけincludeしていいが、ヘッダーファイルでのincludeは極力避けたい。
型定義だけが必要なら、ピンポイントでclass name;で前方宣言したほうがいい。
継承したり、インナークラス、オーバーライドなどする場合はincludeするしかない。
=== lambda ===
ラムダ式 (lambda expression) は関数オブジェクトをその場で定義するための機能。無名関数。
構文
[<capture list>](<parameter list>) mutable <exception> <attribute> -> <return type> { <body> }
以下は省略可能
* (): <parameter list>/mutable/<exception>/<attribute>/<return type> のどれも指定しない場合。
* mutable: キャプチャーした変数を書き換えない場合。
* <exception>: 指定しない場合。
* <attrbute>: 指定しない場合。
* -> <return type>: 型推論に任せる場合。
最小構成は以下となる。
[]{}
キャプチャー
ラムダ式は、ラムダ式外の変数をラムダ式内で参照可能にするキャプチャー (capture) という機能がある。[]内で指定する。
&=参照、==コピーの2個の基本指定があり、以下がある。
* &: 参照。
* =: コピー。
* &x: 変数xを参照。
* x: 変数xをコピー。
* &, x: デフォルトで参照、xのみコピー。
* =, &x: デフォルトでコピー、xのみ参照。
* this: *thisのメンバーを参照。
* this, x: *thisのメンバーを参照、xのみコピー。
== && ==
情報源: [https://stackoverflow.com/questions/5481539/what-does-t-double-ampersand-mean-in-c11 c++ - What does T&& (double ampersand) mean in C++11? - Stack Overflow]。
&&は参照の参照ではない。右辺値参照。ムーブセマンティクスで使用する。


== explicit ==
== explicit ==
4行目: 59行目:


explicitがあるとコピーコンストラクター (=代入) が禁止される。意図しない型変換などを抑止するらしい。
explicitがあるとコピーコンストラクター (=代入) が禁止される。意図しない型変換などを抑止するらしい。
== std::make_unique ==
情報源: [https://ja.stackoverflow.com/questions/24876/make-unique%e3%81%ae%e5%88%a9%e7%82%b9 c++ - make_uniqueの利点 - スタック・オーバーフロー]。
unique_ptr生成時に使う。newするよりいい。処理が1回で済む。
== class ==
=== this ===
出典: [https://learn.microsoft.com/ja-jp/cpp/cpp/this-pointer?view=msvc-170 thisポインター | Microsoft Learn]。
thisはインスタンス内でのみ使用可能な変数。自分のインスタンス自身を指す。
通常使うことはないが、自分のインスタンスを他の関数に引き渡す場合などに使う。
メンバー変数やメンバー関数へのアクセスはthis->が省略されているようなもの。
=== interface ===
情報源:
* [https://marycore.jp/prog/cpp/interface-class-and-duck-typing/ C++ インターフェースの実現方法【インタフェースクラスとダックタイピング】 | MaryCore]
* [https://ja.wikibooks.org/wiki/More_C%2B%2B_Idioms/%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%82%AF%E3%83%A9%E3%82%B9(Interface_Class) More C++ Idioms/インタフェースクラス(Interface Class) - Wikibooks]
C++でJavaなどのinterface (関数、staticのみの派生用クラス) 相当の実現には若干工夫が必要となる。
* 全ての関数が純粋仮想関数: virtual function() = 0
* 仮想デストラクター: virtual ~destructior()
virtualは関数にオーバーライドを許可する。
仮想デストラクターがあることで、派生先側でのデストラクター呼び出しを許容する。逆に、これがないと、該当するデストラクターが呼ばれず、メモリーリークになりえるらしい。
=== abstract ===
情報源:
* [https://kaworu.jpn.org/cpp/%E6%8A%BD%E8%B1%A1%E3%82%AF%E3%83%A9%E3%82%B9 抽象クラス - C++入門]
* [https://stackoverflow.com/questions/40075486/can-a-non-virtual-function-be-equal-to-0 c++ - Can a non-virtual function be equal to 0? - Stack Overflow]
* [https://programming.pc-note.net/cpp/abstract.html 抽象クラス(C++) - 超初心者向けプログラミング入門]
抽象クラス。純粋仮想関数が1個以上存在するクラス。
ベースクラスにvirtual function() = 0;がある場合、派生クラスではvirtualは別になくてもいい。
function() = 0; で同じ意味になる。
純粋仮想関数が存在する場合、継承必須になり、そのクラスはインスタンスを生成できない。
== String ==
出典: [https://cpprefjp.github.io/reference/string.html string - cpprefjp C++日本語リファレンス]
=== Replace ===
std::string::replaceが基本。
文字列の特定範囲を指定文字列で置換する。破壊的操作。
std::findの検索と併用する必要がある。
その他に、algorithmのstd::replaceもある。こちらはcharg型などの1文字同士であれば全置換できる。
findと文字列単位だとwhileなど。
/// "a, b".replaceFirst(", ", "|") 相当の文字列置換
std::string s = "a, b";
std::string t = ", ";  // 検索文字列
auto pos = s.find(t);  // 検索文字列が見つかった位置 (pos == 1)
auto len = t.length(); // 検索文字列の長さ (len == 2)
if (pos != std::string::npos) {​​​​​​​​​​​​​​
  s.replace(pos, len, "|"); // s == "a|b"
}​​​​​​​​​​​​​​
=== Slice ===
特定文字列で検索して、一部分を切り出したいことがよくある。jsonの末端など。
std::string::size_type pos = find(".");
=== startsWith ===
情報源:
* [https://marycore.jp/prog/cpp/starts-with-prefix-search/ C++ 文字列型で前方一致の検索【std::string startsWith/hasPrefix 接頭辞判定】 | MaryCore]。
* [https://stackoverflow.com/questions/1878001/how-do-i-check-if-a-c-stdstring-starts-with-a-certain-string-and-convert-a How do I check if a C++ std::string starts with a certain string, and convert a substring to an int? - Stack Overflow]
std::string s = "abc"; // 検索先の文字列
std::string t = "ab"; // 検索文字列
if (s.size() >= t.size() && std::equal(std::begin(t), std::end(t), std::begin(s))) {​
​ puts("文字列`ab`で始まる文字列です");
}​​
個人的にはこれがシンプルでベストに近いかな。
text.substr(0, start.length()) == start
=== Case ===
情報源
* [https://kaworu.jpn.org/kaworu/2007-04-13-1.php C++ string型の文字列を大文字から小文字へ、小文字から大文字へ変換する]
* [https://ez-net.jp/article/88/fvNmqJRJ/XadygfBS_HpK/ std::string の小文字を大文字に変換する - C++ プログラミング]
#include <algorithm>
std::string string = "";
std::transform(string.cbegin(), string.cend(), string.begin(), std::toupper);
=== contain ===
* [https://stackoverflow.com/questions/2340281/check-if-a-string-contains-a-string-in-c Check if a string contains a string in C++ - Stack Overflow]
* [https://marycore.jp/prog/cpp/std-string-contains/ 【C++】std::stringで文字列が含まれるかどうかの判定【contains】 | MaryCore]
文字列検索。基本はfind。C++23ならstring.containsがある。大文字小文字を無視したければ、事前に処理しておく。
if (s1.find(s2) != std::string::npos) {
    std::cout << "found!" << '\n';
}
== Container ==
=== std::vector ===
==== find ====
vectorの要素検索はそれなりの頻度で必要になる。forで自分で反復させて確認するほかにも方法がある。
std::findとstd::any_ofがある。any_ofは返却値がboolでそのまま条件判定に使えるのでこれが簡単。
出典:
* [https://www.delftstack.com/ja/howto/cpp/cpp-find-element-in-vector/ C++ ベクターに要素が存在するかどうかを調べる方法 | Delft スタック]。
* [https://cpprefjp.github.io/reference/algorithm/find_if.html find_if - cpprefjp C++日本語リファレンス]
findは完全一致でラムダ式が使えないので使いにくい。find_ifはfindのラムダ式が使える版。基本はfind_if。見つかった値をそのまま使える。
    string element_to_check = "nibble";
    vector<string> data_types = {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​"bit", "nibble",
                                 "byte", "char",
                                 "int", "long",
                                 "long long", "float",
                                 "double", "long double"}​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​;
 
    if (any_of(data_types.begin(), data_types.end(),
        [&](const string& elem) {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ return elem == element_to_check; }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​)) {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
        printf("%s is present in the vector\n", element_to_check.c_str());
    }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
  
    if (*find(data_types.begin(), data_types.end(), element_to_check) == element_to_check) {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
        printf("%s is present in the vector\n", element_to_check1.c_str());
    }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ else {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
        printf("%s is not present in the vector\n", element_to_check1.c_str());
    }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
=== std::pair/std::tuple ===
* [https://cpprefjp.github.io/reference/utility/pair.html pair - cpprefjp C++日本語リファレンス]
* [https://cpprefjp.github.io/reference/tuple/tuple.html tuple - cpprefjp C++日本語リファレンス]
* [https://osazo-ml-cv.hatenablog.com/entry/2022/01/29/225021 【C++】std::pair, tupleの基本的な使い方 - osazoの技術ブログ]
キーバリュー形式のデータ型。
std::pairはstd::map類の要素の型。キーバリューで一対一。std::mapは勝手にソートされる。追加順にしたかったら、自分でstd::vector<std::pair>でmapに似た構造を作るしかない。
#include <iostream>
#include <utility>
#include <string>
int main()
{
  // pairオブジェクトの構築
  std::pair<int, std::string> p = std::make_pair(1, "hello");
  // 要素の参照
  std::cout << p.first << std::endl;
  std::cout << p.second << std::endl;
}
std::tupleは一体多の型。std::pairのバリュー部分に独自の構造体を使えば似たようなことはできるが、専用の構造体が不要なのが便利。
#include <iostream>
#include <tuple>
#include <string>
int main()
{
  // 3要素のタプルを作る
  std::tuple<int, char, std::string> t = std::make_tuple(1, 'a', "hello");
  // 0番目の要素を参照
  int& i = std::get<0>(t);
  std::cout << i << std::endl;
  // 2番目の要素を参照
  std::string& s = std::get<2>(t);
  std::cout << s << std::endl;
}
[[Category:IT]]

2024年9月12日 (木) 09:33時点における最新版

About

Core

前方宣言

出典: 【C++】ヘッダー内に他のヘッダーをincludeする量を減らそう : あんちょこ

C/C++がなぜヘッダーとソースファイルを分けているか。ファイル単位の依存関係を減らして、ビルドしやすくするため。

ヘッダーしか存在しない場合、どれか1箇所でも変更すると、全部ビルドが必要になる。

ソースファイルはobjectファイルにコンパイルして、最後にリンクすればいい。

同じ理由で、ソースファイルには好きなだけincludeしていいが、ヘッダーファイルでのincludeは極力避けたい。

型定義だけが必要なら、ピンポイントでclass name;で前方宣言したほうがいい。

継承したり、インナークラス、オーバーライドなどする場合はincludeするしかない。

lambda

ラムダ式 (lambda expression) は関数オブジェクトをその場で定義するための機能。無名関数。

構文

[<capture list>](<parameter list>) mutable <exception> <attribute> -> <return type> { <body> }

以下は省略可能

  • (): <parameter list>/mutable/<exception>/<attribute>/<return type> のどれも指定しない場合。
  • mutable: キャプチャーした変数を書き換えない場合。
  • <exception>: 指定しない場合。
  • <attrbute>: 指定しない場合。
  • -> <return type>: 型推論に任せる場合。

最小構成は以下となる。

[]{}

キャプチャー

ラムダ式は、ラムダ式外の変数をラムダ式内で参照可能にするキャプチャー (capture) という機能がある。[]内で指定する。

&=参照、==コピーの2個の基本指定があり、以下がある。

  • &: 参照。
  • =: コピー。
  • &x: 変数xを参照。
  • x: 変数xをコピー。
  • &, x: デフォルトで参照、xのみコピー。
  • =, &x: デフォルトでコピー、xのみ参照。
  • this: *thisのメンバーを参照。
  • this, x: *thisのメンバーを参照、xのみコピー。

&&

情報源: c++ - What does T&& (double ampersand) mean in C++11? - Stack Overflow

&&は参照の参照ではない。右辺値参照。ムーブセマンティクスで使用する。

explicit

情報源: c++のexplicit指定子の使い方まとめ|コウモリのちょーおんぱ

explicitがあるとコピーコンストラクター (=代入) が禁止される。意図しない型変換などを抑止するらしい。

std::make_unique

情報源: c++ - make_uniqueの利点 - スタック・オーバーフロー

unique_ptr生成時に使う。newするよりいい。処理が1回で済む。

class

this

出典: thisポインター | Microsoft Learn

thisはインスタンス内でのみ使用可能な変数。自分のインスタンス自身を指す。

通常使うことはないが、自分のインスタンスを他の関数に引き渡す場合などに使う。

メンバー変数やメンバー関数へのアクセスはthis->が省略されているようなもの。

interface

情報源:

C++でJavaなどのinterface (関数、staticのみの派生用クラス) 相当の実現には若干工夫が必要となる。

  • 全ての関数が純粋仮想関数: virtual function() = 0
  • 仮想デストラクター: virtual ~destructior()

virtualは関数にオーバーライドを許可する。

仮想デストラクターがあることで、派生先側でのデストラクター呼び出しを許容する。逆に、これがないと、該当するデストラクターが呼ばれず、メモリーリークになりえるらしい。

abstract

情報源:

抽象クラス。純粋仮想関数が1個以上存在するクラス。

ベースクラスにvirtual function() = 0;がある場合、派生クラスではvirtualは別になくてもいい。

function() = 0; で同じ意味になる。

純粋仮想関数が存在する場合、継承必須になり、そのクラスはインスタンスを生成できない。

String

出典: string - cpprefjp C++日本語リファレンス

Replace

std::string::replaceが基本。

文字列の特定範囲を指定文字列で置換する。破壊的操作。

std::findの検索と併用する必要がある。

その他に、algorithmのstd::replaceもある。こちらはcharg型などの1文字同士であれば全置換できる。

findと文字列単位だとwhileなど。

/// "a, b".replaceFirst(", ", "|") 相当の文字列置換
std::string s = "a, b";
std::string t = ", ";  // 検索文字列
auto pos = s.find(t);  // 検索文字列が見つかった位置 (pos == 1)
auto len = t.length(); // 検索文字列の長さ (len == 2)
if (pos != std::string::npos) {​​​​​​​​​​​​​​
  s.replace(pos, len, "|"); // s == "a|b"
}​​​​​​​​​​​​​​

Slice

特定文字列で検索して、一部分を切り出したいことがよくある。jsonの末端など。

std::string::size_type pos = find(".");

startsWith

情報源:

std::string s = "abc"; // 検索先の文字列
std::string t = "ab"; // 検索文字列
if (s.size() >= t.size() && std::equal(std::begin(t), std::end(t), std::begin(s))) {​
​ puts("文字列`ab`で始まる文字列です");
}​​

個人的にはこれがシンプルでベストに近いかな。

text.substr(0, start.length()) == start

Case

情報源

#include <algorithm>

std::string string = "";
std::transform(string.cbegin(), string.cend(), string.begin(), std::toupper);

contain

文字列検索。基本はfind。C++23ならstring.containsがある。大文字小文字を無視したければ、事前に処理しておく。

if (s1.find(s2) != std::string::npos) {
    std::cout << "found!" << '\n';
}

Container

std::vector

find

vectorの要素検索はそれなりの頻度で必要になる。forで自分で反復させて確認するほかにも方法がある。

std::findとstd::any_ofがある。any_ofは返却値がboolでそのまま条件判定に使えるのでこれが簡単。

出典:

findは完全一致でラムダ式が使えないので使いにくい。find_ifはfindのラムダ式が使える版。基本はfind_if。見つかった値をそのまま使える。

    string element_to_check = "nibble";
    vector<string> data_types = {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​"bit", "nibble",
                                 "byte", "char",
                                 "int", "long",
                                 "long long", "float",
                                 "double", "long double"}​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​;
 
    if (any_of(data_types.begin(), data_types.end(),
        [&](const string& elem) {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ return elem == element_to_check; }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​)) {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
        printf("%s is present in the vector\n", element_to_check.c_str());
    }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
  
    if (*find(data_types.begin(), data_types.end(), element_to_check) == element_to_check) {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
        printf("%s is present in the vector\n", element_to_check1.c_str());
    }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ else {​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
        printf("%s is not present in the vector\n", element_to_check1.c_str());
    }​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

std::pair/std::tuple

キーバリュー形式のデータ型。

std::pairはstd::map類の要素の型。キーバリューで一対一。std::mapは勝手にソートされる。追加順にしたかったら、自分でstd::vector<std::pair>でmapに似た構造を作るしかない。

#include <iostream>
#include <utility>
#include <string>

int main()
{
  // pairオブジェクトの構築
  std::pair<int, std::string> p = std::make_pair(1, "hello");

  // 要素の参照
  std::cout << p.first << std::endl;
  std::cout << p.second << std::endl;
}

std::tupleは一体多の型。std::pairのバリュー部分に独自の構造体を使えば似たようなことはできるが、専用の構造体が不要なのが便利。

#include <iostream>
#include <tuple>
#include <string>

int main()
{
  // 3要素のタプルを作る
  std::tuple<int, char, std::string> t = std::make_tuple(1, 'a', "hello");

  // 0番目の要素を参照
  int& i = std::get<0>(t);
  std::cout << i << std::endl;

  // 2番目の要素を参照
  std::string& s = std::get<2>(t);
  std::cout << s << std::endl;
}