📖

C++

に公開
1

エラー

C2440

これは型が不一致の場合に起こるエラーである。

wchar_t* wstr = L"Helloworld";

これを実行すると、

'初期化中': 'const wchar_t [11]' から 'wchar_t *' に変換できません。

というエラーメッセージが表示される。
これは、L"Helloworld"の型がconst wchar_tで、wchar_t *の型に代入しようとしているためにエラーが起きている。
https://chatgpt.com/share/67d64443-eb38-800e-b332-79f51e541c26

C2280

以下のようなvectorを作成し、ビルドした際に発生した

std::vector<std::unique_ptr<int>> test;

std::unique_ptrでさらに囲むとエラーは解決した。

std::unique_ptr<std::vector<std::unique_ptr<int>>> test;

deleteの後にnullptrを代入する理由

ダングリングポインタを防ぐためこれが行われる。

ダングリングポインタのパターン

解放後のポインタの使用

int* ptr = new int(10);
delete ptr;   // メモリを解放
*ptr = 20;    // 未定義動作(解放済みメモリにアクセス)

スコープを抜けたローカル変数の参照

int* getPointer() {
    int localVar = 42;
    return &localVar; // スコープを抜けるとlocalVarは無効になる
}

int* p = getPointer();
std::cout << *p; // 未定義動作(無効なメモリにアクセス)

メモリの二重解放

int* ptr = new int(5);
delete ptr;
delete ptr; // 二重解放(未定義動作)

配列の解放後のアクセス

int* arr = new int[5];
delete[] arr;
std::cout << arr[0]; // 未定義動作

ダングリングポインタを防ぐ方法

解放後のポインタをnullptrに設定

int* ptr = new int(10);
delete ptr;
ptr = nullptr; // 安全策

スマートポインタを活用

#include <memory>

std::unique_ptr<int> ptr = std::make_unique<int>(10);

ポインター

ポインターを引数として渡す

void modify(int* p) {
    *p = 100;  // 呼び出し元の変数を変更
}

int main() {
    int a = 10;
    modify(&a);
    std::cout << "変更後のa: " << a << std::endl;  // 100
}

ポインターを返す関数

int* createInt() {
    int* p = new int(50);
    return p;
}

int main() {
    int* num = createInt();
    std::cout << *num << std::endl;  // 50
    delete num;  // メモリ解放
}

ポインターと動的メモリ確保の違い

記載の仕方

ポインターは、以下のような記載する。

int* p = &a;

動的メモリ確保は、以下のような記載する。

int* p = new int;

メモリの解放

ポインターは、自動(スコープを抜けると解放)。
動的メモリ確保は、delete / delete[] を使って明示的に解放。この処理を行わないとでないとメモリリークが起きる。スマートポインタ(std::unique_ptrやstd::shared_ptr)だとこのdelete処理を行なくて良い。

動的メモリ確保のメリット

https://chatgpt.com/share/67d5262f-6ba4-800e-aa28-bea2af7ba2a2

スマートポインター

メリット

スマートポインターとは、deleteが不要なポインターのことである。
これによって手動でnewするよりもメモリリークなどが起きにくくなる。
https://chatgpt.com/share/67d55421-117c-800e-b5eb-de600c0ecbdf

std::unique_ptrとstd::shared_ptrとstd::weak_ptrについて

スマートポインターには、主に三つある。
std::unique_ptr
std::shared_ptr
std::weak_ptr
である
主な違いは、
所有権を共有である。
https://chatgpt.com/share/67d55421-117c-800e-b5eb-de600c0ecbd
なお、NGコードは以下のようなパターンがある。

std::shared_ptr<int> sp2(new int(10)); 

「コピー不可」とは

「コピー不可」とは、std::unique_ptrで出てくる言葉で以下のようにスマートポインターに他のポインターを代入できないことのことである。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> p1 = std::make_unique<int>(42);
    std::unique_ptr<int> p2 = p1;  // ❌ コピーしようとするとコンパイルエラー
}

https://chatgpt.com/share/67d554bf-77e8-800e-a8af-f4d03777c9a4

循環参照とは

循環参照とは、スマートポインター同士がお互いに参照し合うことで、参照カウントが減らず、メモリが解放されなくなる問題のことです。これによりメモリリーク起きます。
特に C++ の std::shared_ptr を使用している場合に発生しやすいです。
なので、std::shared_ptrを使用する際は、std::weak_ptrと併用して循環参照を防ぐことが多い。
https://chatgpt.com/share/67d55957-dd94-800e-82d8-e8d2a4883304

参照カウントとは

std::shared_ptrを使用する際に出てくる言葉である。
std::shared_ptrが共有しているポインターの数のことである。
https://chatgpt.com/share/67d55d79-07b0-800e-a97d-bc9646a5d6da

lock

lockとは、主にマルチスレッドプログラミングにおいて、ミューテックス(mutex)をロックするためのメソッドとして使用されます。スレッドが共有リソースにアクセスする際に、競合状態(race condition)を防ぐために使われます。
主に4つが存在する。
std::mutex::lock() / unlock()
std::lock_guard
std::unique_lock
std::lock()
https://chatgpt.com/share/67d571f4-82e8-800e-8056-a6d7e8927b10

文字と文字列の型

char

サイズ: 通常 1 バイト(8 ビット)
文字セット: ASCII またはマルチバイト文字(UTF-8, Shift_JIS など)
用途: 英数字やシンプルな文字データに適している。日本語などのマルチバイトは使えない。

char c = 'A';

wchar_t

サイズ: 実装によるが、通常 2 バイト(16 ビット)または 4 バイト(32 ビット)
Windows: 2 バイト(UTF-16)
Linux/macOS: 4 バイト(UTF-32)
文字セット: ワイド文字(UTF-16 や UTF-32 など)
用途: Unicode(多言語対応)を扱う場合に適している。日本語などのマルチバイトは使える。

wchar_t wc = L'あ';  // ワイド文字(Lをつける)

ワイド文字

https://chatgpt.com/share/67d63681-fd70-800e-b402-b4deaf625833

wchar_tとwchar_t*の違い

wchar_tは文字一つ

wchar_t wc = L'A';

wchar_t*は文字列

 wchar_t* wstr = (wchar_t*)L"Hello, world!";

wchar_t* に関連する関数は以下などがある。
wcslen(ptr) : ワイド文字列の長さを取得
wcscpy(dest, src) : ワイド文字列のコピー
wcscmp(str1, str2) : ワイド文字列の比較
swprintf(buf, L"%ls", str) : ワイド文字列のフォーマット

char16_t / char32_t(C++11以降)

Unicodeの UTF-16(char16_t)や UTF-32(char32_t)を扱うための型。

char16_t c16 = u'あ';
char32_t c32 = U'あ';

char の配列

char の配列で文字列を表現する方法。
文字列の終端には \0(ヌル文字)が必要。

char str[] = "Hello";
std::cout << str << std::endl;  // 出力: Hello

上のHelloは実際は以下である。
文字列の終端には \0(ヌル文字)がつく。
なので、sizeofの実行結果は6になる。

'H' 'e' 'l' 'l' 'o' '\0'

std::string

C++標準ライブラリの std::string クラスを使うと、便利な文字列操作が可能。

  • 演算子で結合できる。
std::string s = "Hello";
s += " World!";

std::wstring(ワイド文字列)

std::wstring は wchar_t を使った文字列クラス。
std::wcout を使って出力する。

std::wstring ws = L"こんにちは";
std::wcout << ws << std::endl;

std::stringとstd::wstring(ワイド文字列)の違い

https://chatgpt.com/share/67d65887-8bc4-800e-9b91-c1ceeb4113c5

std::u16string / std::u32string(C++11以降)

Unicodeの UTF-16 や UTF-32 を扱うための文字列クラス。

std::u16string u16s = u"こんにちは";
std::u32string u32s = U"こんにちは";

std::string と char 配列の相互変換

std::string → char*

std::string s = "Hello";
const char* cstr = s.c_str();

char* → std::string

const char* cstr = "Hello";
std::string s = cstr;

newとmalloc()の違い

https://chatgpt.com/share/67d65d56-f298-800e-bfb9-f85087ea7ef6
https://chatgpt.com/share/67d65d61-97ac-800e-9eee-48aca04c6861

memset

memsetは、効率的に初期化、プログラムの安全性を高めたりするのに役立つ。
ただいくつか注意すべき箇所がある。
例えば、メモリオーバーフロー、ポインタの正しい使用、パフォーマンスの低下など。
https://jp-seemore.com/iot/23619/#toc2

_snprintf_s と_wsnprintf_s

_snprintf_sとは

https://chatgpt.com/c/67d688d5-3de8-800e-b72a-08110ddc26fc

_wsnprintf_sとは

https://chatgpt.com/share/67d68ce4-ace0-800e-87dc-bb77a9d9def8

オーバーフロー時に buffer を安全な NULL 終端の空文字列にするとは

https://chatgpt.com/share/67d68d7b-d2e8-800e-9312-bbe32bcfbcdc

バッファサイズの指定方法

https://chatgpt.com/share/67d68e10-caa8-800e-bbe3-f2e517a9eb42
https://chatgpt.com/share/67d68f08-807c-800e-a16e-209eab3604f2
https://chatgpt.com/share/67d6a8c8-e7d8-800e-a3c4-a8e669437a11

typedef structを使用する

typedef struct {
    wchar_t* name;   // wchar_t型の文字列
    int age;         // 整数型の年齢
} Person;

int main() {
    setlocale(LC_ALL, "jpn");

    // Person構造体のインスタンスを作成
    Person person;

    wchar_t* nameValue = (wchar_t*)L"山田太郎";

    // メンバーに値を設定
    person.name = nameValue;  // ワイド文字列リテラル
    person.age = 30;

    // 構造体のメンバーを表示
    wprintf(L"名前: %ls\n", person.name);  // %lsを使ってwchar_t文字列を表示
    wprintf(L"年齢: %d\n", person.age);


    return 0;
}

実行結果

typedefとtypedef structは複数指定できる

typedef

typedef int Integer, *IntPtr, **IntPtr2;
typedef unsigned long ULong, *ULongPtr;

Integer は int 型
IntPtr は int* 型
IntPtr2 は int** 型
ULong は unsigned long 型
ULongPtr は unsigned long* 型

typedef struct

typedef struct {
    int x;
    int y;
} Point, *PointPtr;

Point は構造体型 struct に名前を付けたもの
PointPtr は Point 型へのポインタ

Discussion

Hidden comment
dameyodamedamedameyodamedame

前回も書いていますが、

 wchar_t* wstr = (wchar_t*)L"Hello, world!";

という記述だと非常に危険です。これは絶対にやめましょう。