はじめに
この記事は、
初めてC++を学ぶ方や、既にJavaやC言語を学び、次にC++を学ぶ方
に向けて作成しました。
C++の基礎を学び、JavaやC言語と比較しながらその特徴を理解していきます。
JavaやC言語の基礎知識がある方は、これらを比較しながら学ぶことで、より分かりやすく理解できるでしょう。
まだC言語を学習していない方は、以前に書いたJavaとC言語を比較した記事を参考にすると、理解が深まると思います。
この記事では、詳しいコードの説明には触れていませんので、興味がある方はぜひご自身でさらに調べてみてください。
C++とは?
C++は、C言語を基盤にして開発された強力なプログラミング言語で、オブジェクト指向やジェネリックプログラミングの概念を取り入れた多重パラダイム言語です。
マルチパラダイムプログラミング言語とは複数のプログラミングパラダイムに対応するプログラミング言語の総称です。
マルチパラダイムプログラミング言語の設計目標は、問題解決に当たって最良の道具になることとされております。 代表的なマルチパラダイムプログラミング言語としてJavaScript、C++、C#などが挙げられます。
C++はC言語の拡張版であり、大規模なソフトウェア開発に向けて効率的に設計されています。
Javaと同様にオブジェクト指向を採用していますが、C言語との高い互換性を持ち、システムプログラミングにも適しています。
ポインタや直接メモリアドレス操作が可能で、ハードウェア制御やパフォーマンスチューニングが容易です。
C++の長所と短所
-
長所:
-
C言語との互換性があり、既存のCコードを再利用可能。
-
フレームワークに依存せず、C言語よりもセキュリティが強化されている。
-
高いパフォーマンスと低レベルなメモリ操作が可能。
-
-
短所:
-
言語仕様が複雑で、標準で利用できるライブラリが限られている。
-
メモリ管理を手動で行う必要がある。1
-
活用例
-
ゲーム開発
高速な処理と低レベルのハードウェア制御が可能なため、ゲームエンジンやリアルタイムレンダリングで広く使用されています。
例:Unreal Engine、Unityのネイティブコード部分 -
GUIアプリケーション
高度なUI設計が可能で、クロスプラットフォームの開発がしやすいため、デスクトップアプリケーションに利用されます。
今はバックエンド部分をC++で、フロントエンド部分をTypescriptで書くなどが多いですが、パフォーマンスを考えるとまだまだC++が有利な部分も多いです。
例:Adobe Photoshop、Visual Studio -
ハイパフォーマンスコンピューティング(HPC)
大規模な科学計算やシミュレーションなど、計算資源を効率的に使用する必要がある分野で使用されます。
例:天気予報シミュレーション、粒子物理シミュレーション -
システムソフトウェア開発
OSやドライバなど、ハードウェアに近い層での制御が必要なソフトウェアに使用されます。
例:Windows、Linuxカーネルの一部 -
金融システム
高い性能と信頼性が求められるトレーディングシステムやデータベース管理に使用されます。
Java、C言語、C++の比較表
項目 | C言語 | C++ | Java |
---|---|---|---|
開発者 | Dennis Ritchie(AT&Tベル研究所) | Bjarne Stroustrup | James Gosling(サン・マイクロシステムズ) |
リリース年 | 1972 | 1985 | 1995 |
オブジェクト指向 | ✖ | 〇 | 〇 |
メモリ管理 | 手動(malloc, free) | 手動(new, delete)1 | 自動(ガベージコレクション) |
標準ライブラリ | 標準Cライブラリ(stdio.hなど) | STL(など) | Java API(java.utilなど) |
ポイント操作 | あり | あり | なし |
多重継承 | なし | あり(注意が必要) | なし(インターフェースで代替) |
詳細解説:C++ vs Java vs C
オブジェクト指向の比較
-
Javaはすべてがクラスの中に存在し、トップレベルの関数や変数がありません。C++では、クラス以外にトップレベルの関数や変数を定義することが可能です。
-
Javaではメモリ管理が自動化されていますが、C++では手動でのメモリ管理が必要であるため、より細かな制御が可能です。
-
C言語は手続き型言語なのでオブジェクト指向言語ではありません。
(それっぽい事はできます)
メモリ管理とガベージコレクション
-
C++の手動メモリ管理
C++では、new
キーワードでメモリを動的に割り当て、delete
で解放する必要があります。このため、メモリリークのリスクがある一方で、メモリ管理を自分でコントロールできる利点もあります。1 -
Javaのガベージコレクション
Javaはガベージコレクションを使用してメモリを自動管理します。プログラマが手動でメモリを解放する必要がないため、メモリリークのリスクが減少します。
関数オーバーロードと演算子オーバーロード
-
C++は関数や演算子のオーバーロードをサポートしています。同じ名前の関数や演算子に対して、異なる引数の組み合わせで複数の定義を持つことができます。
-
Javaでは関数のオーバーロードが可能ですが、演算子のオーバーロードはサポートされていません。
-
C言語には、関数や演算子のオーバーロードはサポートされていません。
基本構文
ファイル構成
C言語やC++では、コードをヘッダファイル(.h
)とソースファイル(.cpp
)(C言語は.c
)に分けて管理します。これに対して、Javaはパッケージとクラス単位で管理し、publicが付いたクラス名はクラス名とファイル名を一致させる必要があります。
文字列操作
C言語では、char型の配列を使って文字列を扱いますが、C++ではより便利なstd::stringクラスを使用します。これにより、文字列操作が簡単かつ直感的に行えるようになります。
同様に、JavaでもStringクラスを使用します。C++やJavaでは、+演算子を使って簡単に文字列を連結することができます。
#include <iostream>
#include <string>
int main() {
std::string str1 = "Hello";
std::string str2 = "World";
std::string str3 = str1 + " " + str2;
std::cout << str3 << std::endl;
return 0;
}
Hello World!
標準入出力
C++の標準出力にはstd::cout
、標準入力にはstd::cin
を使用します。
これに対し、JavaではSystem.out.println
やScanner
クラスを使用します。
C言語で必要だった%d
や¥n
などのデータ型指定が不要になり、コードが簡潔になっています。
また、データの入出力には<<
,>>
を使用します。これらはストリームと呼ばれ、データの読み書きを直感的に扱うための仕組みです。
#include <iostream>
int main() {
std::string name;
int age;
// ユーザーに名前と年齢の入力を求める
std::cout << "名前を入力してください: ";
std::cin >> name;
std::cout << "年齢を入力してください: ";
std::cin >> age;
// 入力された情報を表示
std::cout << "こんにちは、" << name << "さん!あなたは" << age << "歳です。" << std::endl;
return 0;
}
#include <stdio.h>
int main() {
char name[50];
int age;
// ユーザーに名前と年齢の入力を求める
printf("名前を入力してください: ");
scanf("%49s", name); // %49sはバッファのサイズを制限して入力
printf("年齢を入力してください: ");
scanf("%d", &age);
// 入力された情報を表示
printf("こんにちは、%sさん!あなたは%d歳です。\n", name, age);
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// ユーザーに名前と年齢の入力を求める
System.out.print("名前を入力してください: ");
String name = scanner.nextLine();
System.out.print("年齢を入力してください: ");
int age = scanner.nextInt();
// 入力された情報を表示
System.out.println("こんにちは、" + name + "さん!あなたは" + age + "歳です。");
scanner.close(); // リソースを解放
}
}
参照渡しとポインタ
C++では、関数に引数を渡す際に参照を使ってその値を直接変更することができます。
C言語ではポインタを使って同様の効果を実現します。
一方Javaには参照渡しはありません。配列やオブジェクトなどの参照型の変数を扱う場合、参照渡しの様に2つの変数間で同じインスタンスを共有している動きが見られる、別名参照問題と知られているものはあります。
// 参照を使って値を変更する関数
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
オブジェクト指向
オブジェクト指向 (Object Oriented) とは、操作や処理の対象を モノ として表現し、そのモノに対して指示を送ることで操作や処理を行わせる、 という考え方です。
C++はオブジェクト指向プログラミング(OOP)をサポートしています。
Javaでも同様のオブジェクト指向の考え方が採用されています。
C言語は手続き型プログラミングであり、オブジェクト指向の特徴を持っていません。
クラスとオブジェクト
C++におけるクラスは、オブジェクトの設計図であり、データ(メンバー変数)とそれに関連する操作(メンバ関数)をひとつにまとめて管理します。
クラスを使用することで、複雑なデータ構造とその操作を効果的に組織化することができます。
C言語を理解している方が最初にクラスをイメージする際には、
クラス = 「構造体」+「関数」
と考えると良いでしょう。
「構造体」: 複数のデータをひとつのパッケージとしてまとめる機能
「関数」: データを処理する機能
これらを1つにまとめて管理するのが「クラス」の基本的な考え方です。
以下の例では、Car
クラスがspeed
という属性を持ち、accelerate
というメンバ関数を使ってspeed
を増加させることができます。
クラスを使うことで、データとその操作を1つのまとまりとして扱えるため、プログラムの設計がより直感的で効率的になります。
class Car {
public:
int speed; // 属性(データ)
void accelerate() { // メンバ関数(操作)
speed += 10;
}
};
コンストラクタとデストラクタ
C++では、クラスのインスタンス生成時に自動的に呼ばれるコンストラクタや、インスタンスが破棄される際に自動的に呼ばれるデストラクタが存在します。
これらは、メンバ変数の初期化やリソースの解放に使われます。
ただし、コンストラクタやデストラクタは必ず定義する必要はなく、C++の標準機能により、自動生成される場合もあります。
例えば、以下のようなコードでもコンパイルは正常に行われます。
class Car {
public:
int speed = 0; // 自動的に0で初期化
void accelerate() {
speed += 10;
}
};
コンストラクタやデストラクタは、必要に応じて定義することで、初期化やリソース管理を明示的に行うことができます。
だいぶ省略していますが、例えば次のように定義することが可能です。
class Car {
public:
Car() { // コンストラクタ
speed = 0;
}
~Car() { // デストラクタ
// リソースの解放など
}
int speed;
void accelerate() {
speed += 10;
}
};
メモリ管理
C++では、new
とdelete
を使って動的にメモリを管理します。
一方、Javaではガーベジコレクションが自動的にメモリ管理を行います。
C++ではメモリリークを防ぐために、適切にdelete
を呼び出す必要があります。
しかし、手動でnew
やdelete
を使う場合、誤ってメモリを解放し忘れたり、二重解放のリスクがあります。
これを避けるために、スマートポインタというC++標準ライブラリの機能が提供されています。スマートポインタは、動的に確保したメモリの所有権を自動的に管理し、不要になった際にメモリを自動的に解放してくれます。
アクセス指定子
アクセス指定子(Javaでは「アクセス修飾子」と呼ばれる)は、クラスのメンバ(変数や関数)に対してアクセス権を制御するために使われます。
C++とJavaの両方でアクセス権を制御しますが、C++にはクラスそのものにはアクセス指定子を付けることができない点が異なります。
Javaでも
public
、private
、protected
を使いますが、クラス自体にもアクセス修飾子を付けることができる点が異なります。例えば、public
クラスは他のパッケージからアクセスできるように定義することができます。
class Car {
public: // クラス外からアクセス可能
int speed;
private: // クラス外部からアクセスできない
int enginePower;
protected: // 派生クラスからアクセス可能
void boost();
};
継承
継承は、既存のクラス(基底クラス)の機能を新しいクラス(派生クラス)に引き継ぐ機能です。これにより、コードの再利用が促進され、オブジェクト指向プログラミングの基本的な設計パターンを実現できます。
C++では、多重継承(複数の基底クラスから継承すること)も可能ですが、注意が必要です。多重継承は強力な機能ですが、設計が複雑になりやすく、ダイヤモンド継承(同じ基底クラスが複数の経路で継承されること)による問題を引き起こす可能性があります。このような場合には、仮想継承を用いて基底クラスを明示的に1つに統一する方法などが推奨されます。
以下の例では、ElectricCar
クラスがCar
クラスを基底クラスとして継承し、Car
クラスの機能を持ちつつ、新たにメンバ関数charge
を追加しています。
class ElectricCar : public Car {
public:
void charge() {
// 電池を充電する処理
}
};
オーバーロード
オーバーロードは、同じ名前のメンバ関数や演算子を異なる引数リストで定義する機能です。
以下の例では、メンバ関数add
が異なる引数リストを持ち、整数と浮動小数点数の加算をサポートしています。
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
};
このように、C++ではオブジェクト指向の概念が中心にあり、クラスとオブジェクトを使った設計が可能です。
生成と消去、メモリ管理、継承、オーバーロードなどの機能を駆使することで、柔軟で再利用性の高いコードを書くことができます。
C++の特有の概念(JavaとC言語と比べて)
名前空間(Namespace)
名前空間は、プログラム内で同じ名前の識別子が衝突するのを防ぐ仕組みです。
複数のライブラリやコードが同じ名前の変数や関数を使うと、名前が衝突する可能性があります。名前空間を使うことで、こうした衝突を防ぎ、コードの整理がしやすくなります。
C言語には名前空間の概念が存在せず、プレフィックスを使って識別子を手動で管理する必要があります。
Javaでは、packageという概念が名前空間に似ています。クラスやインターフェースを異なるパッケージに分けることで、名前の衝突を防ぎますが、Javaのパッケージはクラス単位であり、C++の名前空間は関数や変数、クラスなど、より広範囲に適用できます。
C++では、namespace
を使用して名前空間を定義します。
以下の例では、MyNamespace
という名前空間内にmyFunction
という関数を定義しています。
#include <iostream>
namespace MyNamespace {
void myFunction() {
std::cout << "MyNamespaceからこんにちは!" << std::endl;
}
}
int main() {
// 名前空間を指定して関数を呼び出す
MyNamespace::myFunction();
return 0;
}
テンプレート(Template)
テンプレートは、型に依存しない汎用的なコードを記述するための仕組みです。これにより、同じコードを異なるデータ型に対して使い回すことができます。
テンプレートは、関数やクラスの設計を型に依存しない形で行えるため、コードの再利用性が高まります。
C言語にはテンプレートの概念がなく、型に応じた異なる関数や構造体を個別に定義する必要があります。
Javaにはジェネリクスという似た機能がありますが、C++のテンプレートとはいくつかの違いがあります。ジェネリクスは型安全であり、実行時に型が確定しますが、C++のテンプレートはコンパイル時に型が決まります。
以下の例では、add
というテンプレート関数を定義しています。この関数は、任意の型(T
)の引数を2つ受け取り、その合計を返します。
#include <iostream>
// テンプレート関数の定義
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add(3, 4) << std::endl; // 整数型で使用
std::cout << add(2.5, 3.1) << std::endl; // 浮動小数点型で使用
return 0;
}
STL(Standard Template Library)
STLは、C++の標準ライブラリで、テンプレートを利用して様々なデータ構造やアルゴリズムを提供します。
C言語では、これらのデータ構造やアルゴリズムは標準ライブラリには含まれていません。C言語の標準ライブラリには、配列やリストなどの基本的なデータ構造が含まれていますが、高度なデータ構造やアルゴリズムは自分で実装する必要があります。
Javaでは、ArrayListやHashMapなどのデータ構造が標準ライブラリとして提供されていますが、C++のSTLはより広範囲にわたるテンプレートベースのライブラリです。
以下の例では、std::vector
を使って整数の動的配列を作成し、std::sort
アルゴリズムでソートしています。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 3, 4, 1, 2}; // 動的配列の作成
std::sort(numbers.begin(), numbers.end()); // ソート
// ソートされた結果を表示
for (int num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
まとめ
いかがでしたでしょうか。主にC++の基本的なこと
、Java、C言語、C++の比較
、オブジェクト指向
、C++の特有の概念
などについてまとめてみました。
C++は、C言語のパワフルな機能を継承しつつ、オブジェクト指向の特性を持つ言語です。Javaと比較して、低レベルな制御が可能であり、C言語との互換性を持ちながらも、強力な標準ライブラリやテンプレート機能を提供しています。
以上になりますが、ここまでご閲覧ありがとうございました!
参考資料
厳選!C++ アルゴリズム実装に使える 25 の STL 機能【前編】
C++ クラス【オブジェクト指向を最初に学ぶためのイメージ】