C++で本格的なアプリケーションを作るためには、クラス(class)とオブジェクトの基本を理解することが欠かせません。
本記事では、クラスの定義方法とオブジェクトの生成(インスタンス化)を、初心者向けに順を追って丁寧に説明します。
構文の最小形からメンバの追加、宣言と定義の分離、そして関数に渡す基本まで、実行可能なサンプルコードで確認していきます。
C++のクラスとオブジェクトの基本
クラス(class)とは
クラスとは、データとそれに関連する操作(関数)をひとまとまりに表す設計図です。
C++では、クラスを使うことでプログラムの構造を整理し、再利用しやすくできます。
クラスの中には、メンバ変数(状態)とメンバ関数(振る舞い)を定義します。
また、public
やprivate
などのアクセス指定子で外部に公開する範囲を制御します。
以下は概念の要点です。
- クラス = 設計図(何を持ち、何ができるかを定義)
- オブジェクト = その設計図から作られた実体
- アクセス指定子でカプセル化(データ隠蔽)を行い、使いやすく安全なAPIを提供します
オブジェクト(インスタンス)とは
オブジェクトとは、クラスから実際に生成された値(実体)のことです。
例えばclass Car {...};
という設計図からCar myCar;
のように作られる具体物がオブジェクトです。
プログラム実行時には、オブジェクトはメモリ上に配置され、.
(ドット演算子)でメンバにアクセスします。
クラスとオブジェクトの関係
クラスは型、オブジェクトはその型の値です。
つまり「int型とintの値」の関係に似ています。
クラスでできることは、インスタンス(オブジェクト)として具体的なデータと振る舞いを持ちます。
次の対応で理解すると把握しやすいです。
- クラス: 型(何を持つか、どう振る舞うかを定義)
- オブジェクト: 値(定義に基づく具体的なデータと機能)
- 複数のオブジェクトは同じクラスから何個でも生成可能
クラス(class)の作り方(基本構文)
最小のclass定義
まずは最小の形を確認します。
クラスは末尾にセミコロンが必要です。
// 最小のクラス定義の例
class Empty {
// 何も持たないクラス
}; // ← このセミコロンを忘れない
この状態でもEmpty e;
のようにオブジェクトを生成できます。
もっとも、何も持たないので使い道は限られます。
メンバ変数とメンバ関数の追加
次に、状態と振る舞いを持つクラスを作ります。
初心者が最初に身につけたいのは、メンバ変数は原則private、操作はpublicにするという基本方針です。
// カウンタを表すクラスの例
#include <iostream>
using namespace std;
class Counter {
private:
int value; // カウンタの現在値(外部から直接は触らせない)
public:
// 初期化用の関数(本記事では簡単のために関数で初期化)
void init(int start) {
value = start; // メンバ変数に代入
}
// 値を1つ増やす
void increment() {
value = value + 1;
}
// 現在値を読み出す
int get() {
return value;
}
};
int main() {
Counter c; // オブジェクトを生成
c.init(10); // 初期化
c.increment();// 1増やす
cout << c.get() << endl; // 期待される出力: 11
}
11
ここでは簡単のため、初期化はメンバ関数init
で行いました。
C++にはコンストラクタ
という専用の初期化機能もありますが、詳細は別記事で扱います。
宣言と定義の分け方(入門)
規模が大きくなると、クラスの「宣言(インタフェース)」と「定義(実装)」を分けるのが一般的です。
ヘッダファイルにクラスの宣言、ソースファイルにメンバ関数の定義を書きます。
以下はCounter
を分割した例です。
// Counter.hpp (ヘッダファイル)
// クラスの宣言(インタフェース)
#ifndef COUNTER_HPP
#define COUNTER_HPP
class Counter {
private:
int value;
public:
void init(int start);
void increment();
int get();
};
#endif // COUNTER_HPP
// Counter.cpp (実装ファイル)
// メンバ関数の定義(本体)
#include "Counter.hpp"
void Counter::init(int start) {
value = start;
}
void Counter::increment() {
value = value + 1;
}
int Counter::get() {
return value;
}
// main.cpp (利用側)
// クラスを使うコード
#include <iostream>
#include "Counter.hpp"
using namespace std;
int main() {
Counter c;
c.init(3);
c.increment();
cout << c.get() << endl; // 期待: 4
}
4
この分割により、使う側はヘッダだけを見れば「何ができるか」を把握できるようになります。
実装の変更に強く、再コンパイル時間の短縮にも役立ちます。
クラス末尾の;(セミコロン)に注意
クラス定義の締めには必ずセミコロン;
が必要です。
class Example {
// クラスの中身あれこれ
}; // ← このセミコロンを忘れない
関数定義の締めには不要なので、クラス定義だけ特別と覚えておくとミスを減らせます。
オブジェクトの作り方(インスタンス化と使い方)
自動変数でオブジェクト生成
最も基本的な生成法は、自動記憶域(ローカル変数)として宣言することです。
関数のスコープを抜けると自動的に破棄されます。
// 自動変数としてのオブジェクト生成
#include <iostream>
using namespace std;
class Point {
public:
int x;
int y;
};
int main() {
Point p; // 自動変数として生成
p.x = 2;
p.y = 5;
cout << "(" << p.x << "," << p.y << ")" << endl;
}
(2,5)
ここでは説明の都合でメンバをpublic
にしています。
実務では原則private
にしてアクセサ関数を用意することが多いです。
ドット演算子でメンバにアクセス
オブジェクトのメンバには.
でアクセスします。
ドット演算子は値そのもの(オブジェクト)からメンバを取り出すと覚えるとよいでしょう。
#include <iostream>
using namespace std;
class Greeter {
private:
string name;
public:
void setName(const string& s) {
name = s;
}
void sayHello() {
cout << "Hello, " << name << "!" << endl;
}
};
int main() {
Greeter g; // オブジェクト生成
g.setName("C++");
g.sayHello(); // ドット演算子でメンバ関数呼び出し
}
Hello, C++!
関数に渡して使う(値渡しの基本)
関数の引数にクラス型を指定すると、デフォルトでは「値渡し(コピー)」になります。
つまり、関数内では元のオブジェクトのコピーが使われ、呼び出し元には影響しません。
#include <iostream>
using namespace std;
class Counter {
private:
int value = 0;
public:
void increment() { value = value + 1; }
int get() { return value; }
};
// 値渡し: 引数cは呼び出し元の「コピー」
void addThree(Counter c) {
c.increment();
c.increment();
c.increment();
cout << "[addThree内] c = " << c.get() << endl;
}
int main() {
Counter c;
c.increment(); // 1回だけ増やす (c = 1)
addThree(c); // 値渡し(コピー)なので呼び出し元cは変化しない
cout << "[main内] c = " << c.get() << endl;
}
[addThree内] c = 3
[main内] c = 1
この動作により、呼び出し元のデータを保護できる一方、コピーが高コストの型ではパフォーマンスに影響することがあります。
性能や意図に応じて参照渡しを使う方法もありますが、詳細は別記事で扱います。
小さなサンプルで流れを確認
クラス定義→オブジェクト生成→メンバ呼び出し
最後に、1ファイルで完結する小さな流れのサンプルを示します。
クラス定義→オブジェクト生成→メンバ呼び出しの一連の動きを確認しましょう。
// Sample: 1つのクラスを作り、使うまでの最短経路
#include <iostream>
using namespace std;
// 2Dベクトルの長さを求めたいが、まずは大枠だけ作る例
class Vector2 {
private:
double x;
double y;
public:
// 簡易初期化用の関数(本記事ではコンストラクタを避けて関数で説明)
void set(double x_, double y_) {
x = x_;
y = y_;
}
// x, y を読み出す関数
double getX() { return x; }
double getY() { return y; }
// 文字列表現を表示する
void print() {
cout << "Vector2(" << x << ", " << y << ")" << endl;
}
};
int main() {
Vector2 v; // 1) オブジェクト生成
v.set(3.0, 4.0);// 2) 状態を設定
v.print(); // 3) メンバ関数を呼び出し
cout << "x=" << v.getX() << ", y=" << v.getY() << endl;
}
Vector2(3, 4)
x=3, y=4
この例ではデータはprivateに、操作はpublicに置いて、外からの直接書き換えを防ぐ設計にしています。
これがクラス設計の基本スタイルです。
よくあるミスのチェックリスト
初心者がつまずきやすい点を整理します。
短くても、ここに注意するだけでトラブルが大幅に減ります。
- クラス定義の末尾のセミコロン
;
の付け忘れ private
にしたメンバ変数へ外部から直接アクセスしようとしてコンパイルエラー- メンバ関数の定義をクラス外に書く際、
ClassName::
(スコープ解決演算子)を付け忘れる - 変数の初期化を忘れて未定義の値を使ってしまう
std::string
や入出力を使うのに#include <string>
や#include <iostream>
を忘れる
これらはエラーメッセージと対応付けて一つずつ解消すると、上達が早まります。
まとめ
本記事では、C++のクラス(class)の作り方とオブジェクト生成の基本を、最小構文からメンバの追加、宣言と定義の分離、そして値渡しまで一通り解説しました。
ポイントは次の通りです。
クラスは設計図、オブジェクトは実体であり、データはprivate、操作はpublicにするのが基本です。
オブジェクトは自動変数として簡単に生成でき、ドット演算子でメンバにアクセスできます。
セミコロンの付け忘れやアクセス指定の誤りなどの初歩的なミスに注意しながら、小さなクラスから手を動かして慣れていきましょう。
次のステップとして、初期化専用のコンストラクタや後片付けのデストラクタ、参照渡しなども学ぶと、より堅牢で効率的なコードが書けるようになります。