第1章:C++基礎とOOP概念
はじめに
CPP Module 00は、C++プログラミングへの第一歩です。このモジュールでは、CからC++への移行に必要な基礎概念を学びます。
本章では、名前空間、クラス、メンバ関数、iostream、static、constなど、C++の基本概念をコンピュータサイエンスの観点から解説します。
---
1. CからC++へ
1.1 なぜC++か
C++は1979年にBjarne StroustrupがC言語を拡張して開発しました。彼の目標は:
> "C with Classes: a language that provides Simula-like classes for C." > > (C with Classes: CにSimulaのようなクラスを提供する言語)
C++がCに追加した主な機能:
- クラスとオブジェクト
- 名前空間
- 関数オーバーロード
- 参照
- 例外処理
- テンプレート
1.2 コンパイルの違い
# C言語
gcc -Wall -Wextra -Werror program.c -o program
# C++
c++ -Wall -Wextra -Werror -std=c++98 program.cpp -o program
1.3 ファイル拡張子
| 言語 | ソースファイル | ヘッダファイル |
|------|--------------|--------------|
| C | .c | .h |
| C++ | .cpp, .cxx, .cc | .hpp, .hxx, .h |
---
2. 名前空間(Namespace)
2.1 問題:名前の衝突
大規模プログラムでは、同じ名前の関数や変数が衝突する可能性があります:
/* C言語 - 問題のあるコード */
/* library_a.c */
void print() { printf("Library A\n"); }
/* library_b.c */
void print() { printf("Library B\n"); } /* リンクエラー! */
2.2 解決:名前空間
C++では名前空間で識別子をグループ化できます:
namespace LibraryA {
void print() { std::cout << "Library A" << std::endl; }
}
namespace LibraryB {
void print() { std::cout << "Library B" << std::endl; }
}
int main() {
LibraryA::print(); // Library A
LibraryB::print(); // Library B
return 0;
}
2.3 スコープ解決演算子(::)
::演算子は、名前空間やクラスのメンバにアクセスするために使用します:
// 名前空間のメンバにアクセス
std::cout << "Hello" << std::endl;
// グローバル名前空間へのアクセス
::globalFunction();
// クラスの静的メンバにアクセス
MyClass::staticMethod();
2.4 using宣言とusingディレクティブ
// using宣言:特定の名前を現在のスコープに導入
using std::cout;
using std::endl;
cout << "Hello" << endl; // std:: なしで使用可能
// usingディレクティブ:名前空間全体を導入(推奨されない)
using namespace std;
cout << "Hello" << endl; // 名前衝突のリスクがある
ベストプラクティス: ヘッダファイルではusing namespaceを使用しない。
2.5 標準名前空間 std
C++標準ライブラリの全ての機能はstd名前空間に定義されています:
std::string // 文字列クラス
std::vector // 動的配列
std::cout // 標準出力
std::cin // 標準入力
std::endl // 改行とフラッシュ
---
3. クラスの基本
3.1 構造体からクラスへ
C言語の構造体はデータをグループ化するだけでしたが、C++のクラスはデータと関数を一つにまとめます:
/* C言語の構造体 */
struct Point {
int x;
int y;
};
void point_print(struct Point* p) {
printf("(%d, %d)\n", p->x, p->y);
}
// C++のクラス
class Point {
int x;
int y;
public:
void print() {
std::cout << "(" << x << ", " << y << ")" << std::endl;
}
};
3.2 クラスの定義
class ClassName {
private:
// プライベートメンバ(クラス外からアクセス不可)
int privateData;
protected:
// プロテクテッドメンバ(派生クラスからアクセス可能)
int protectedData;
public:
// パブリックメンバ(誰からでもアクセス可能)
int publicData;
void publicMethod();
};
3.3 structとclassの違い
C++ではstructとclassはほぼ同じですが、デフォルトのアクセス指定子が異なります:
struct S {
int x; // デフォルトでpublic
};
class C {
int x; // デフォルトでprivate
};
3.4 カプセル化
David Parnasの情報隠蔽の原則に基づき、内部実装を隠してインターフェースだけを公開します:
class BankAccount {
private:
double balance; // 内部実装を隠蔽
public:
// 公開インターフェース
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
bool withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
double getBalance() const {
return balance;
}
};
---
4. コンストラクタとデストラクタ
4.1 コンストラクタ
オブジェクト生成時に自動的に呼ばれる特殊な関数:
class Contact {
private:
std::string firstName;
std::string lastName;
public:
// デフォルトコンストラクタ
Contact() : firstName(""), lastName("") {
std::cout << "Default constructor called" << std::endl;
}
// パラメータ付きコンストラクタ
Contact(const std::string& first, const std::string& last)
: firstName(first), lastName(last) {
std::cout << "Parameterized constructor called" << std::endl;
}
};
int main() {
Contact c1; // デフォルトコンストラクタ
Contact c2("John", "Doe"); // パラメータ付きコンストラクタ
return 0;
}
4.2 初期化リスト
メンバを効率的に初期化するための構文:
class Example {
private:
const int id; // constメンバ
std::string& ref; // 参照メンバ
std::string name;
public:
// 初期化リストが必須
Example(int i, std::string& r, const std::string& n)
: id(i), ref(r), name(n) {
// ここでは代入しかできない(初期化は終わっている)
}
};
初期化リストを使う理由:
- constメンバの初期化
- 参照メンバの初期化
- 効率性(代入より速い)
- 基底クラスの初期化
4.3 デストラクタ
オブジェクト破棄時に自動的に呼ばれる関数:
class Resource {
private:
int* data;
public:
Resource(int size) {
data = new int[size];
std::cout << "Resource acquired" << std::endl;
}
~Resource() {
delete[] data;
std::cout << "Resource released" << std::endl;
}
};
int main() {
{
Resource r(100); // コンストラクタ呼び出し
// ... 使用 ...
} // スコープ終了:デストラクタ自動呼び出し
return 0;
}
---
5. メンバ関数
5.1 クラス内定義
class Counter {
private:
int value;
public:
// クラス内で定義(インライン化される可能性)
void increment() {
value++;
}
int getValue() const {
return value;
}
};
5.2 クラス外定義
// ヘッダファイル (Counter.hpp)
class Counter {
private:
int value;
public:
void increment();
int getValue() const;
};
// ソースファイル (Counter.cpp)
void Counter::increment() {
value++;
}
int Counter::getValue() const {
return value;
}
5.3 constメンバ関数
オブジェクトを変更しない関数にはconstを付けます:
class Point {
private:
int x, y;
public:
// constメンバ関数(オブジェクトを変更しない)
int getX() const { return x; }
int getY() const { return y; }
// 非constメンバ関数(オブジェクトを変更する)
void setX(int newX) { x = newX; }
};
5.4 thisポインタ
メンバ関数内で自分自身を指すポインタ:
class Counter {
private:
int value;
public:
Counter& increment() {
this->value++;
return *this; // 自分自身を返す(メソッドチェーン)
}
};
// 使用例
Counter c;
c.increment().increment().increment(); // valueは3になる
---
6. iostream
6.1 出力(std::cout)
#include <iostream>
int main() {
// 基本的な出力
std::cout << "Hello, World!" << std::endl;
// 複数の値を連結
int age = 25;
std::string name = "Alice";
std::cout << "Name: " << name << ", Age: " << age << std::endl;
// 改行
std::cout << "Line 1" << std::endl; // 改行 + フラッシュ
std::cout << "Line 2\n"; // 改行のみ
return 0;
}
6.2 入力(std::cin)
#include <iostream>
#include <string>
int main() {
// 整数の入力
int number;
std::cout << "Enter a number: ";
std::cin >> number;
// 文字列の入力(スペースまで)
std::string word;
std::cout << "Enter a word: ";
std::cin >> word;
// 行全体の入力
std::string line;
std::cin.ignore(); // 前の改行を消費
std::cout << "Enter a line: ";
std::getline(std::cin, line);
return 0;
}
6.3 フォーマット
#include <iostream>
#include <iomanip>
int main() {
// 幅指定
std::cout << std::setw(10) << 42 << std::endl; // " 42"
// 右寄せ/左寄せ
std::cout << std::setw(10) << std::right << 42 << std::endl; // " 42"
std::cout << std::setw(10) << std::left << 42 << std::endl; // "42 "
// 埋め文字
std::cout << std::setfill('0') << std::setw(5) << 42 << std::endl; // "00042"
return 0;
}
---
7. staticメンバ
7.1 静的メンバ変数
クラスの全インスタンスで共有される変数:
class Counter {
private:
static int count; // 宣言のみ
int id;
public:
Counter() : id(++count) {
std::cout << "Object " << id << " created" << std::endl;
}
static int getCount() {
return count;
}
};
// 定義(クラス外で必要)
int Counter::count = 0;
int main() {
Counter c1; // Object 1 created
Counter c2; // Object 2 created
Counter c3; // Object 3 created
std::cout << "Total: " << Counter::getCount() << std::endl; // 3
return 0;
}
7.2 静的メンバ関数
インスタンスなしで呼び出せる関数:
class Math {
public:
static int add(int a, int b) {
return a + b;
}
static int abs(int x) {
return x < 0 ? -x : x;
}
};
int main() {
int sum = Math::add(3, 5); // 8
int absolute = Math::abs(-10); // 10
return 0;
}
注意: 静的メンバ関数内ではthisポインタを使用できません。
---
8. const修飾子
8.1 const変数
const int MAX_SIZE = 100; // 変更不可
// MAX_SIZE = 200; // エラー!
8.2 constポインタ
int x = 10;
int y = 20;
const int* ptr1 = &x; // 指す先がconst
// *ptr1 = 30; // エラー
ptr1 = &y; // OK
int* const ptr2 = &x; // ポインタ自体がconst
*ptr2 = 30; // OK
// ptr2 = &y; // エラー
const int* const ptr3 = &x; // 両方const
// *ptr3 = 30; // エラー
// ptr3 = &y; // エラー
8.3 const参照
void printValue(const std::string& str) {
std::cout << str << std::endl;
// str = "modified"; // エラー:変更不可
}
---
まとめ
本章で学んだこと:
次章では、これらの概念を使ってCPP Module 00の課題を実装します。