MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
C++の基礎 - 左辺値 と 左辺値参照のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
C++の基礎 - 左辺値 と 左辺値参照
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == 左辺値 (lvalue) は、C言語から存在する基本的な概念であり、メモリ上の特定の場所を持つオブジェクトを指す。<br> C++では、左辺値参照 (lvalue reference) を使用して、オブジェクトへの別名を作成できる。<br> <br><br> == 左辺値 (lvalue) == ==== 定義 ==== 左辺値とは、メモリ上の特定のアドレスを持つオブジェクトを指す。<br> 名前を持ち、プログラム実行中に識別可能なエンティティである。<br> <br> ==== 特徴 ==== 左辺値は、以下に示す特徴を持つ。<br> * 名前を持つ変数や、参照、デリファレンス演算子(*)を使用した式等が該当する。 * 代入演算子の左側に現れることができる。 * &演算子を使用して、そのアドレスを取得できる。 * プログラム実行中、一定期間メモリ上に存在し続ける。 * 識別可能であり、複数回参照できる。 <br> ==== 左辺値の例 ==== <syntaxhighlight lang="c++"> #include <iostream> class MyClass { public: int value; MyClass(int v) : value(v) {} }; int globalVar = 100; // グローバル変数は左辺値 int main() { // 変数は左辺値 int x = 10; int y = 20; // 配列要素は左辺値 int arr[5] = {1, 2, 3, 4, 5}; arr[2] = 30; // オブジェクトのメンバは左辺値 MyClass obj(42); obj.value = 50; // ポインタのデリファレンスは左辺値 int* ptr = &x; *ptr = 15; // 配列名は左辺値 int* arrPtr = arr; // 文字列リテラルは左辺値(特殊なケース) const char* str = "Hello"; // "Hello"は左辺値 // 関数は左辺値 int (*funcPtr)() = globalVar; // 関数へのポインタ // すべての左辺値はアドレスを取得できる int* xAddr = &x; int* yAddr = &y; int* arrElemAddr = &arr[0]; int* objValueAddr = &(obj.value); return 0; } </syntaxhighlight> <br> ==== 左辺値になるもの ==== 以下に示すものは左辺値である。<br> * 変数名 * 配列要素 * クラスのメンバ変数 * ポインタのデリファレンス * 文字列リテラル *: const char[]型 * 左辺値参照を返す関数呼び出し * 前置インクリメント・デクリメント *: ++x, --x * 代入式 *: 例 : int x = y <br> ==== 左辺値にならないもの ==== 以下に示すものは左辺値ではなく、右辺値である。<br> * 数値リテラル *: 例 : 42, 3.14等 * 算術演算の結果 *: x + y * 非参照を返す関数呼び出し * 後置インクリメント・デクリメント *: x++, x-- * 一時オブジェクト <br><br> == 左辺値参照 (lvalue reference) == ==== 定義 ==== 左辺値参照は、既存のオブジェクトへの別名 (エイリアス) である。<br> C++98から存在する従来の参照型であり、<u>型名&</u> という記法で表現される。<br> <br> ==== 基本的な構文 ==== <syntaxhighlight lang="c++"> int x = 10; int& ref = x; // 左辺値参照の宣言と初期化 const int& cref = x; // const左辺値参照 </syntaxhighlight> <br> ==== 左辺値参照の特性 ==== 左辺値参照は、以下に示す特性を持つ。<br> * 必ず初期化時に束縛する必要がある。 * 1度束縛すると、別のオブジェクトに再束縛できない。 * 参照自体はメモリを持たない。(コンパイラによる最適化の対象) * 参照を通じた操作は、束縛したオブジェクトへの操作と同じである。 * 左辺値参照は、左辺値のみを束縛できる。 * const左辺値参照は、左辺値と右辺値の両方を束縛できる。 <br> ==== 使用例 ==== <syntaxhighlight lang="c++"> #include <iostream> int main() { int x = 10; int& ref = x; // 左辺値参照の作成 std::cout << "x: " << x << std::endl; // 10 std::cout << "ref: " << ref << std::endl; // 10 // 参照を通じた値の変更 ref = 20; std::cout << "x after ref = 20: " << x << std::endl; // 20 // 変数を通じた値の変更 x = 30; std::cout << "ref after x = 30: " << ref << std::endl; // 30 // 同じアドレスを指している std::cout << "Address of x: " << &x << std::endl; std::cout << "Address of ref: " << &ref << std::endl; return 0; } </syntaxhighlight> <br> ==== 左辺値参照の束縛 ==== <syntaxhighlight lang="c++"> #include <iostream> int main() { int x = 10; int y = 20; // 左辺値参照は左辺値を束縛できる int& ref1 = x; // OK // 左辺値参照は右辺値を束縛できない // int& ref2 = 10; // コンパイルエラー // int& ref3 = x + y; // コンパイルエラー // 参照の「代入」は、束縛先の値を変更する int& ref2 = y; ref1 = ref2; // xの値がyの値になる(ref1がref2を指すわけではない) std::cout << "x: " << x << std::endl; // 20 std::cout << "y: " << y << std::endl; // 20 // 参照を再束縛することはできない // ref1はxへの別名であり続ける return 0; } </syntaxhighlight> <br> ==== const左辺値参照 ==== const左辺値参照は、特別な性質を持つ。<br> <br> <syntaxhighlight lang="c++"> #include <iostream> #include <string> void printValue(const int& value) { std::cout << "Value: " << value << std::endl; } void printString(const std::string& str) { std::cout << "String: " << str << std::endl; } int main() { int x = 10; // const左辺値参照は左辺値を束縛できる const int& cref1 = x; // const左辺値参照は右辺値を束縛できる const int& cref2 = 20; // OK const int& cref3 = x + 5; // OK // const左辺値参照を通じた値の変更はできない // cref1 = 30; // コンパイルエラー // 元の変数を通じた値の変更は可能 x = 30; std::cout << "cref1 after x = 30: " << cref1 << std::endl; // 30 // 関数の引数としてのconst左辺値参照 printValue(x); // 左辺値を渡す printValue(42); // 右辺値を渡す printValue(x + 10); // 式の結果(右辺値)を渡す std::string str = "Hello"; printString(str); // 左辺値を渡す printString("World"); // 右辺値を渡す return 0; } </syntaxhighlight> <br> ==== 一時オブジェクトの寿命延長 ==== const左辺値参照に束縛された一時オブジェクトは、参照のスコープが終了するまで寿命が延長される。<br> <br> <syntaxhighlight lang="c++"> #include <iostream> #include <string> class TempObject { public: TempObject() { std::cout << "TempObject created" << std::endl; } ~TempObject() { std::cout << "TempObject destroyed" << std::endl; } void use() const { std::cout << "Using TempObject" << std::endl; } }; TempObject createObject() { return TempObject(); } int main() { std::cout << "--- Without reference ---" << std::endl; createObject().use(); // 一時オブジェクトはすぐに破棄される std::cout << "\n--- With const lvalue reference ---" << std::endl; const TempObject& ref = createObject(); // 寿命が延長される ref.use(); std::cout << "Still in scope" << std::endl; // スコープ終了時に破棄される return 0; } </syntaxhighlight> <br><br> == メソッドの引数としての左辺値参照 == ==== 参照渡しによる効率化 ==== 大きなオブジェクトを関数に渡す時、左辺値参照を使用することでコピーを避けられる。<br> <br> <syntaxhighlight lang="c++"> #include <iostream> #include <vector> // 値渡し : コピーが発生する void processByValue(std::vector<int> vec) { std::cout << "Vector size: " << vec.size() << std::endl; } // 左辺値参照渡し : コピーが発生しない void processByReference(std::vector<int>& vec) { std::cout << "Vector size: " << vec.size() << std::endl; } // const左辺値参照渡し : コピーが発生せず、変更もできない void processByConstReference(const std::vector<int>& vec) { std::cout << "Vector size: " << vec.size() << std::endl; } int main() { std::vector<int> largeVec(1000000, 42); processByValue(largeVec); // コピーが発生 (遅い) processByReference(largeVec); // コピーなし (速い) processByConstReference(largeVec); // コピーなし(速い)、変更不可 return 0; } </syntaxhighlight> <br> ==== 入出力パラメータ ==== 左辺値参照を使用して、メソッド内でオブジェクトを変更できる。<br> <br> <syntaxhighlight lang="c++"> #include <iostream> #include <string> // 出力パラメータ void split(const std::string& fullName, std::string& firstName, std::string& lastName) { size_t pos = fullName.find(' '); if (pos != std::string::npos) { firstName = fullName.substr(0, pos); lastName = fullName.substr(pos + 1); } } // 入出力パラメータ void appendSuffix(std::string& str, const std::string& suffix) { str += suffix; } int main() { std::string fullName = "John Doe"; std::string first, last; split(fullName, first, last); std::cout << "First: " << first << ", Last: " << last << std::endl; std::string message = "Hello"; appendSuffix(message, " World!"); std::cout << message << std::endl; // Hello World! return 0; } </syntaxhighlight> <br> ==== 関数の戻り値としての左辺値参照 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <vector> class Container { private: std::vector<int> data; public: Container() : data{1, 2, 3, 4, 5} {} // 左辺値参照を返す int& at(size_t index) { return data.at(index); } // const左辺値参照を返す const int& at(size_t index) const { return data.at(index); } }; int main() { Container c; // 左辺値参照を返すため、直接変更可能 c.at(0) = 10; std::cout << c.at(0) << std::endl; // 10 // 連鎖代入も可能 c.at(1) = c.at(2) = 20; std::cout << c.at(1) << ", " << c.at(2) << std::endl; // 20, 20 // const版の使用 const Container constC; int value = constC.at(0); // OK // constC.at(0) = 100; // コンパイルエラー return 0; } </syntaxhighlight> <br><br> == 左辺値参照の実用例 == ==== 範囲for文での使用 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <vector> int main() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // コピーが発生する for (auto num : numbers) { num *= 2; // ローカルコピーを変更 (元のベクタは変わらない) } // 参照を使用 (元のベクタを変更) for (auto& num : numbers) { num *= 2; // 元のベクタの要素を変更 } // const参照を使用 (変更不可、コピーなし) for (const auto& num : numbers) { std::cout << num << " "; // 読み取り専用 } std::cout << std::endl; return 0; } </syntaxhighlight> <br> ==== メンバ変数としての左辺値参照 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <string> class Logger { private: std::ostream& output; // 左辺値参照をメンバに持つ public: // コンストラクタで参照を初期化(必須) Logger(std::ostream& out) : output(out) {} void log(const std::string& message) { output << "[LOG] " << message << std::endl; } }; int main() { Logger consoleLogger(std::cout); consoleLogger.log("This goes to console"); std::ofstream file("log.txt"); Logger fileLogger(file); fileLogger.log("This goes to file"); return 0; } </syntaxhighlight> <br> ==== スワップ操作 ==== <syntaxhighlight lang="c++"> #include <iostream> #include <utility> // 左辺値参照を使用したスワップ void swap(int& a, int& b) { int temp = a; a = b; b = temp; } // より効率的なムーブを使用したスワップ template<typename T> void swapMove(T& a, T& b) { T temp = std::move(a); a = std::move(b); b = std::move(temp); } int main() { int x = 10, y = 20; std::cout << "Before swap: x = " << x << ", y = " << y << std::endl; swap(x, y); std::cout << "After swap: x = " << x << ", y = " << y << std::endl; std::string s1 = "Hello", s2 = "World"; std::cout << "Before swap: s1 = " << s1 << ", s2 = " << s2 << std::endl; swapMove(s1, s2); std::cout << "After swap: s1 = " << s1 << ", s2 = " << s2 << std::endl; return 0; } </syntaxhighlight> <br><br> == その他 == ==== ダングリング参照 ==== 参照が指すオブジェクトのスコープが終了すると、ダングリング参照(dangling reference)が発生する。<br> <br> <syntaxhighlight lang="c++"> #include <iostream> int& getDanglingReference() { int local = 42; return local; // 危険: ローカル変数への参照を返す } int main() { // int& ref = getDanglingReference(); // 未定義動作 // std::cout << ref << std::endl; // 危険 return 0; } </syntaxhighlight> <br> ==== 参照の初期化は必須 ==== <syntaxhighlight lang="c++"> int main() { // int& ref; // コンパイルエラー: 参照は初期化が必須 int x = 10; int& ref = x; // OK return 0; } </syntaxhighlight> <br> ==== 参照の配列は作成できない ==== <syntaxhighlight lang="c++"> int main() { int x = 10, y = 20, z = 30; // int& refs[3] = {x, y, z}; // コンパイルエラー: 参照の配列は不可 // ポインタの配列は可能 int* ptrs[3] = {&x, &y, &z}; // OK return 0; } </syntaxhighlight> <br> ==== nullptrや未初期化の参照は存在しない ==== <syntaxhighlight lang="c++"> int main() { // 参照はnullにできない // int& ref = nullptr; // コンパイルエラー // ポインタとの違い int* ptr = nullptr; // OK: ポインタはnullにできる return 0; } </syntaxhighlight> <br><br> == 左辺値参照とポインタの比較 == 左辺値参照とポインタは似ているが、重要な違いがある。<br> <br> <center> {| class="wikitable" style="background-color:#fefefe;" |- ! 特性 !! 左辺値参照 !! ポインタ |- | 初期化 || 必須 || 任意 |- | null値 || 不可 || 可能 |- | 再代入 || 不可 (束縛先の値を変更) || 可能 (別のアドレスを指す) |- | 構文 || シンプル (オブジェクトと同じ) || *演算子が必要 |- | オーバーヘッド || 通常なし (最適化される) || あり (間接参照) |- | 配列 || 作成不可 || 作成可能 |- | ポインタ演算 || 不可 || 可能 |} </center> <br> <syntaxhighlight lang="c++"> #include <iostream> int main() { int x = 10; int y = 20; // 左辺値参照 int& ref = x; ref = y; // xの値が20になる (refがyを指すわけではない) std::cout << "x: " << x << std::endl; // 20 // ポインタ int* ptr = &x; ptr = &y; // ptrがyを指すようになる std::cout << "x: " << x << std::endl; // 20(変わらない) return 0; } </syntaxhighlight> <br><br> == 最適化とインライン展開 == コンパイラは、左辺値参照を最適化することが多い。<br> 特に、小さな関数では、参照がインライン展開され、実質的なオーバーヘッドが無くなる。<br> <br> <syntaxhighlight lang="c++"> inline int& getMax(int& a, int& b) { return (a > b) ? a : b; } int main() { int x = 10, y = 20; // インライン展開により、効率的なコードが生成される getMax(x, y) = 30; // yが30になる return 0; } </syntaxhighlight> <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:C++]]
C++の基礎 - 左辺値 と 左辺値参照
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse