Unity_Lesson

プログラミング言語には、低レベル言語や高レベル言語などの分類があり、メモリ管理や書き方・用途・できること・できないことに違いがあります。

低レベル・高レベルの意味について


以下で、C、C++、Java、C#、JavaScript、Python、Dartなどの主要な言語を比較して解説します。


1. C

例:

  #include <stdlib.h>  // mallocとfreeのために必要なヘッダーをインクルード

  int main() {
      int *p = malloc(sizeof(int));// メモリを動的に確保する(int型1つ分のサイズを確保)
      *p = 10;// 確保したメモリに10を代入
      free(p);// 確保したメモリを解放する
      return 0;// 正常終了を示すために0を返す
  }


2. C++

例:

  #include <iostream>

  int main() {
      int* p = new int(10);// ヒープ領域にint型のメモリを確保し、値10を初期化
      delete p;// 確保したメモリを解放
      return 0;// 正常終了を示すために0を返す
  }


3. Java

例:

public class Main {
    public static void main(String[] args) {
        // Integerクラスのオブジェクトを新規に生成しています。
        // ここで "new Integer(10)" は、ヒープ領域に数値10を持つIntegerオブジェクトを作成します。
        Integer num = new Integer(10);
        
        // しかし、Javaでは、オートボクシングという機能により
        // "new Integer(10)" の代わりに、単に "Integer num = 10;" と書けます。
        // これは、基本データ型の値(int型の10)が自動的にIntegerオブジェクトに変換されるためです。
        // そのため、"new Integer(10)" を使うケースは今ではほとんどなく、非推奨となっています。

        // メモリ管理:
        // Javaにはガベージコレクタ(GC)があるため、C++のように手動でdeleteを行う必要はありません。
        // num変数がスコープを抜けた時点や不要になった時点で、GCがこのオブジェクトを検出し、
        // メモリを自動で解放してくれます。
    }
}


スタックとヒープについて


4. C#

例:

using System;

class Program {
    static void Main() {
        // ここでは、int型の変数numを宣言し、値10を代入しています。
        int num = 10;

        // intはC#の基本型(値型)であり、スタックメモリに格納されます。
        // ヒープではなく、スタックに格納されるため、特にメモリ管理の心配はありません。
        // num変数がスコープを抜ける(このMainメソッドが終了する)と、スタックメモリから自動的に解放されます。

        // C#では、メモリ管理は基本的にガベージコレクタ(GC)が自動で行います。
        // 基本型(int, floatなど)はスタックに置かれますが、オブジェクト型(クラスなどのインスタンス)は
        // ヒープに置かれ、GCが不要になったタイミングで自動解放します。
    }
}

例:

using System;
using System.Collections.Generic;

class Task {
    public string Name { get; set; }
    public bool IsCompleted { get; private set; }

    public Task(string name) {
        Name = name;
        IsCompleted = false;
    }

    public void Complete() {
        IsCompleted = true;
        Console.WriteLine($"タスク '{Name}' が完了しました。");
    }

    public override string ToString() {
        return $"{Name} - {(IsCompleted ? "完了" : "未完了")}";
    }
}

class Program {
    static void Main() {
        List<Task> tasks = new List<Task>();
        string command;

        Console.WriteLine("タスク管理アプリにようこそ!");

        do {
            Console.WriteLine("\nコマンドを入力してください:");
            Console.WriteLine("1: タスクを追加する");
            Console.WriteLine("2: タスクを完了する");
            Console.WriteLine("3: タスクを表示する");
            Console.WriteLine("4: 終了する");
            command = Console.ReadLine();

            switch (command) {
                case "1":
                    Console.Write("追加するタスクの名前を入力してください: ");
                    string taskName = Console.ReadLine();
                    tasks.Add(new Task(taskName));
                    Console.WriteLine($"タスク '{taskName}' を追加しました。");
                    break;

                case "2":
                    Console.Write("完了するタスクのインデックスを入力してください (0から始まる番号): ");
                    if (int.TryParse(Console.ReadLine(), out int index) && index >= 0 && index < tasks.Count) {
                        tasks[index].Complete();
                    } else {
                        Console.WriteLine("無効なインデックスです。");
                    }
                    break;

                case "3":
                    Console.WriteLine("\nタスクリスト:");
                    for (int i = 0; i < tasks.Count; i++) {
                        Console.WriteLine($"{i}: {tasks[i]}");
                    }
                    break;

                case "4":
                    Console.WriteLine("アプリを終了します。");
                    break;

                default:
                    Console.WriteLine("無効なコマンドです。");
                    break;
            }
        } while (command != "4");
    }
}

[C#実行例](/Unity_Lesson/1_ElementaryKnowledge/CS%E5%AE%9F%E8%A1%8C%E4%BE%8B.html)



・unityでの使用例

ScoreManager,cs

using UnityEngine;
using UnityEngine.UI;

public class ScoreManager : MonoBehaviour
{
    public static ScoreManager Instance; // シングルトンインスタンス
    public int Score { get; private set; } = 0; // スコア
    public Text scoreText; // UIテキストオブジェクト

    private void Awake()
    {
        // シングルトンの実装
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject); // シーンをまたいでオブジェクトを保持
        }
        else
        {
            Destroy(gameObject); // 既に存在する場合は削除
        }
    }

    // スコアを加算するメソッド
    public void AddScore(int points)
    {
        Score += points;
        UpdateScoreText();
    }

    // スコアテキストを更新するメソッド
    private void UpdateScoreText()
    {
        scoreText.text = "Score: " + Score;
    }
}


PlayerController.cs

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private void Update()
    {
        // スペースキーを押したときにスコアを加算
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ScoreManager.Instance.AddScore(10); // スコアを10加算
        }
    }
}



5. JavaScript

例:

  let num = 10;
  console.log(num);

例:


function createObject() {
    // スタックにローカル変数が格納される
    const localConstant = "I am constant"; // スタックに格納され、再代入不可
    let localVariable = 10; // スタックに格納され、再代入可能

    // ヒープにオブジェクトを作成
    const myObject = {
        message: "I am an object",
        value: 42
    }; // `const` なので、プロパティの変更は可能だが、再代入は不可

    console.log(localConstant); // スタックから値を取得
    console.log(localVariable); // スタックから値を取得
    console.log(myObject.message); // ヒープからオブジェクトのプロパティを取得

    // `let`で宣言した変数の再代入
    localVariable += 5;
    console.log("Updated localVariable:", localVariable); // 15 が出力される

    // オブジェクトのプロパティを更新
    myObject.value += 10;
    console.log("Updated myObject.value:", myObject.value); // 52 が出力される

    // myObjectの参照を返す
    return myObject;
}

// createObjectが呼ばれ、ローカル変数がスタックに追加される
const obj = createObject();
console.log(obj.value); // ヒープに格納されたオブジェクトのプロパティを参照(52 が出力される)



htmlと一緒に使った例:

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>タスク管理アプリ</title>
    <style>
        body { font-family: Arial, sans-serif; }
        ul { list-style-type: none; padding: 0; }
        li { padding: 8px; margin: 4px 0; background-color: #f0f0f0; }
        button { margin-left: 10px; }
    </style>
</head>
<body>
    <h1>タスク管理アプリ</h1>
    <input type="text" id="taskInput" placeholder="新しいタスクを追加">
    <button id="addTaskBtn">タスクを追加</button>

    <h2>タスクリスト</h2>
    <ul id="taskList"></ul>

    <script>
        // タスクリストを保持する配列
        let tasks = [];

        // タスクを追加する関数
        function addTask() {
            const input = document.getElementById('taskInput');
            const task = input.value.trim();//文字列の前後にある空白(スペース、タブ、改行など)を取り除く

            if (task) {
                // タスクを配列に追加
                tasks.push(task);
                input.value = ''; // 入力フィールドをクリア
                renderTasks(); // タスクリストを再描画
            } else {
                alert('タスクを入力してください。');
            }
        }

        // タスクリストを描画する関数
        function renderTasks() {
            const taskList = document.getElementById('taskList');
            taskList.innerHTML = ''; // 既存のリストをクリア

            // タスクをリストに追加
            tasks.forEach((task, index) => {
                const li = document.createElement('li');
                li.textContent = task;

                // 削除ボタンを作成
                const deleteBtn = document.createElement('button');
                deleteBtn.textContent = '削除';
                deleteBtn.onclick = () => deleteTask(index);

                li.appendChild(deleteBtn);
                taskList.appendChild(li);
            });
        }

        // タスクを削除する関数
        function deleteTask(index) {
            tasks.splice(index, 1); // 配列からタスクを削除
            renderTasks(); // タスクリストを再描画
        }

        // ボタンのクリックイベントを設定
        document.getElementById('addTaskBtn').addEventListener('click', addTask);
    </script>
</body>
</html>



6. Python

例:

class Counter:
    def __init__(self):
        self._count = 0  # 初期カウント値を0に設定

    def increment(self):
        """カウントを増加させるメソッド"""
        self._count += 1
        print(f"Current count: {self._count}")

    def reset(self):
        """カウントをリセットするメソッド"""
        self._count = 0
        print("Counter reset to 0.")

    def get_count(self):
        """現在のカウント値を取得するメソッド"""
        return self._count


def main():
    counter = Counter()  # Counterクラスのインスタンスを作成

    while True:
        action = input("Enter 'i' to increment, 'r' to reset, or 'q' to quit: ").lower()

        if action == 'i':
            counter.increment()  # カウントを増加
        elif action == 'r':
            counter.reset()  # カウントをリセット
        elif action == 'q':
            print(f"Final count: {counter.get_count()}")  # 最終カウントを表示
            break  # ループを終了
        else:
            print("Invalid input. Please enter 'i', 'r', or 'q'.")


if __name__ == "__main__":
    main()


例:

class MyObject:
    def __init__(self, message, value):
        # ヒープに格納されるオブジェクトの属性を初期化
        self.message = message
        self.value = value

    def display_message(self):
        # ヒープからオブジェクトのメッセージを取得して表示
        print(f"Message: {self.message}")

    def update_value(self, new_value):
        # オブジェクトの値を更新
        self.value = new_value
        print(f"Value updated to: {self.value}")

def create_object():
    # スタックにローカル変数が格納される
    local_variable = "I am local"  # スタックに格納される
    print(local_variable)  # スタックから値を取得

    # ヒープにオブジェクトを作成
    my_object = MyObject("I am an object", 42)  # ヒープに格納される
    my_object.display_message()  # ヒープからオブジェクトのプロパティを取得

    # my_objectの参照を返す
    return my_object

# メイン処理
if __name__ == "__main__":
    obj = create_object()  # create_objectが呼ばれ、ローカル変数がスタックに追加される
    print(f"Initial value: {obj.value}")  # ヒープに格納されたオブジェクトのプロパティを参照
    obj.update_value(100)  # オブジェクトの値を更新



7. Dart

例:


class Counter {
  int _count = 0; // 初期値を0に設定

  // 現在のカウント値を取得するメソッド
  int get count => _count;

  // カウントを増加させるメソッド
  void increment() {
    _count++;
    print("Current count is: $_count");
  }

  // カウントをリセットするメソッド
  void reset() {
    _count = 0;
    print("Counter reset. Current count is: $_count");
  }
}

void main() {
  Counter counter = Counter(); // Counterクラスのインスタンスを作成

  counter.increment(); // カウントを1増加
  counter.increment(); // カウントをさらに1増加
  print("Final count after increments: ${counter.count}"); // 現在のカウントを出力

  counter.reset(); // カウントをリセット
}


例:


class MyObject {
  String _message;
  int _value;

  MyObject(this._message, this._value);

  // メッセージの取得
  String get message => _message;

  // 値の取得と更新
  int get value => _value;
  set value(int newValue) {
    _value = newValue;
    print("MyObject value updated to: $_value");
  }

  // メッセージを表示するメソッド
  void displayMessage() {
    print("Message: $_message");
  }
}

void createObject() {
  // 定数として再代入不可のローカル変数
  final String localConstant = "I am a constant"; 
  // 再代入可能なローカル変数
  String localVariable = "I am a local variable"; 

  // オブジェクトを作成(プロパティは変更可能)
  final MyObject myObject = MyObject("I am an object", 42); 

  print(localConstant); // 定数を出力
  print(localVariable); // ローカル変数を出力
  myObject.displayMessage(); // オブジェクトのメッセージを出力

  // 再代入可能なローカル変数の更新
  localVariable = "Updated local variable";
  print(localVariable); // 更新後のローカル変数

  // プロパティの更新と出力
  myObject.value = 100;
  print("Current myObject.value: ${myObject.value}");
}

void main() {
  createObject();
}



まとめ

言語 レベル メモリ管理 書き方の特徴 できること できないこと
C 低-中 手動管理 ハードウェア寄り、ポインタ使用 組み込み、OS、デバイスドライバ 高レベルの抽象化、手動管理の手間が大きい
C++ 中間 手動&自動(スマートポインタ) オブジェクト指向、テンプレート ゲーム開発、アプリ、ハードウェア制御 自動GCなし、メモリリークに注意が必要
Java 自動(GC) オブジェクト指向、クラスベース エンタープライズ、Web、Android システム操作の低レベル制御が困難
C# 自動(GC) .NET統合、シンプル デスクトップ、Web、ゲーム(Unity) Windows向けが強い
JavaScript 自動(GC) Web向け、スクリプト言語 Webアプリ、サーバーサイド(Node.js) 低レベル操作が困難
Python 自動(GC) インデントベース、シンプル データサイエンス、AI、Web パフォーマンスが低い、低レベル操作が苦手
Dart 自動(GC) モダン、Flutterで使用 クロスプラットフォームのアプリ開発 低レベル操作が困難、Flutter以外での普及がまだ少ない


CPPとC#の関数に焦点を当てて比較してみる。