1. 文字列プリミティブ リテラル
R "()" はエスケープ解除に使用され、パス表現に使用できます
正常に実行されました
これら 2 つの RawValue は説明的な役割を果たし (書き込むことはできません)、出力には関与しません。
ここでは中国語の文字化けが出力されることに注意してください
2、nullptr
NULL は C++ では 0 を意味し、非 C++ ではユニバーサル ポインターを意味します
nullptr は 0 ではなく null ポインターであり、さまざまなタイプのポインターと互換性がありますが、空です
3、constexpr
定数を定義する場合、const と constexpr は同等です
関数を constexpr で修飾する場合、関数は可能な限りコンパクトにする必要があります。
1. 非定数式以外の文は使用できません(using ディレクティブ、typedef ディレクティブ、static_assert、return 文を除く)。
たとえば、for ループは使用できません。
シナリオ: 単純な計算
4. auto および decltype 型の推論
auto は配列を定義できません:
auto a[10] = { 1, 2, 3 };//書き方が間違っています
auto によって推定されるオブジェクトは、同時に宣言して代入する必要があります (例: auto a = 3;)が、直接 auto a ではありません。これは、 a の型がこの 3 によって推定され、decltype が必要ないためです。
UE4 の場合:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "list"
#include "iostream"
#include "GlobeAwareDefaultPawn.h"
#include "GlobeAwareActor.generated.h"
UCLASS()
class UNREALEARTHLIBRARY_API AGlobeAwareActor : public AGlobeAwareDefaultPawn
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AGlobeAwareActor();
virtual void BeginPlay() override;
private:
std::list<int> int_list;
};
template<typename T>
class Test {
public:
void Print(T a) {
for (templateIter = a.begin(); templateIter != a.end(); templateIter++) {
PrintNumOnScreen(*templateIter);
}
}
void PrintNumOnScreen(FString str) {
GEngine->AddOnScreeenDebugMessage(-1, 10.f, FColor::Red, str);
}
void PrintNumOnScreen(int value) {
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, FString::FromInt(value));
}
private:
decltype(T().begin()) templateIter;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "GlobeAwareActor.h"
AGlobeAwareActor::AGlobeAwareActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
int_list = { 1, 2, 3, 4 };
}
void AGlobeAwareActor::BeginPlay() {
Super::BeginPlay();
Test<std::list<int>> *test = new Test<std::list<int>>();
test->Print(int_list);
}
コンパイルは成功し、実行時の効果は次のようになります。
T にコンストラクターがない場合、関数の戻り値を再配置する必要があります。ここでは例として TArray<int> を取り上げます。
auto Function->decltype(TArray<int>) {}
5. Final で変更された関数は、仮想関数で書き換えたり継承したりすることはできません。
このとき、Childから継承した後はテストメソッドを書き換えることはできません、よく言われるように、finalは子と孫を切る、うーん、覚えやすいです
Childクラスの後にfinalを追加します。これ以降はクラスを継承できません
6、オーバーライド
クラス B がクラス A から継承し、型 B がクラス A とまったく同じメソッドを定義したい場合、クラス A のメソッドに仮想装飾がある場合、動作は書き換えになります。仮想装飾がない場合は、動作が書き換えられます。メソッド名の意味だけで他が異なる場合はオーバーライドといい、それ以外はオーバーロードといいます。
7. 山括弧
たとえば、元の TArray<list<int>> a では、ここの >> が曖昧になるため、4>>2 の演算記号は TArray<list<int>> a の前にスペースを追加して区別する必要があります。ただし、C++11 以降は、TArray<list<int>> として記述できます。
8. テンプレートのデフォルトタイプ
型を渡すことで型を決定できるだけでなく、上記のように型推論のためにオブジェクト パラメータを渡すこともできます。
9. 関数の使用
①名前空間
使用シナリオ: C/C++ には多数の変数、関数、クラスがあり、これらの変数、関数、クラスの名前はすべてグローバル スコープ内に存在するため、多くの競合が発生する可能性があります。 名前空間を使用する目的は、名前の競合や名前汚染を避けるために識別子の名前をローカライズすることです。
名前空間 std を使用します。
② 型エイリアス(および typedef の意味)を定義する
MY_INT = int を使用します。
typedef int MY_INT;
概要: を使用するとより直感的に見えます
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
using func = void(*)(FString);
#define NONE LogChoice::NA
#define WARNING LogChoice::Warning
#define ERROR LogChoice::Error
#define DISPLAY LogChoice::Display
#define RED FColor::Red
#define GREEN FColor::Green
#define CYAN FColor::Cyan
#define BLUE FColor::Blue
#define YELLOW FColor::Yellow
#define BLACK FColor::Black
#define SILVER FColor::Silver
#define WHITE FColor::White
#define PURPLE FColor::Purple
#define ORANGE FColor::Orange
enum LogChoice : uint8 {
NA,
Warning,
Error,
Display
};
namespace DebugHelper {
void WarningLog(FString text) {
UE_LOG(LogTemp, Warning, TEXT("%s"), *text);
}
void ErrorLog(FString text) {
UE_LOG(LogTemp, Error, TEXT("%s"), *text)
}
void DisplayLog(FString text) {
UE_LOG(LogTemp, Display, TEXT("%s"), *text);
}
void Printf(float time, FColor color, float text_float, bool isUseLog = false, LogChoice logChoice = NONE) {
FString FloatToString = FString::SanitizeFloat(text_float);
func logFunc = NULL;
if (isUseLog) {
switch (logChoice) {
case NONE: {
break;
}
case WARNING: {
logFunc = WarningLog;
break;
}
case ERROR: {
logFunc = ErrorLog;
break;
}
case DISPLAY: {
logFunc = DisplayLog;
break;
}
default: {
}
}
logFunc(FloatToString);
}
GEngine->AddOnScreenDebugMessage(-1, time, color, FloatToString);
}
void Printf(float time, FColor color, FString text, bool isUseLog = false, LogChoice logChoice = LogChoice::NA) {
func logFunc = NULL;
if (isUseLog) {
switch (logChoice) {
case NONE: {
break;
}
case WARNING: {
logFunc = WarningLog;
break;
}
case ERROR: {
logFunc = ErrorLog;
break;
}
case DISPLAY: {
logFunc = DisplayLog;
break;
}
default: {
}
}
logFunc(text);
}
GEngine->AddOnScreenDebugMessage(-1, time, color, text);
}
};
10. コンストラクターの委任
2 番目のコンストラクターには最初のコンストラクターが含まれるため、次のように記述できます。
委任された構築は、他のコンストラクター内にネストされたコンストラクターを使用します。
11. 継承されたコンストラクター
これは、次のコードを記述することと同じです。
コードを書く速度が向上します。Child は Base の int m_i、double m_j、string m_k を継承するため、これらの変数も Child で初期化する必要があるため、親クラスのコンストラクターの代入を記述する必要がありますが、これは非常に面倒です。 Base::Base を使用して直接置き換えることができます。これで終わりです。これが継承コンストラクターです。
12. 初期化リスト
13、std::initializer::list
任意の数の同一の引数を受け取る機能
#include "iostream"
using namespace std;
template<typename T>
void func(std::initializer_list<T> llist) {
auto it = llist.begin();
for (; it != llist.end(); it++) {
cout << *it << endl;
}
}
int main() {
func<int>({ 1,2,3,4,5 });
func<string>({ "123","534" });
return 0;
}
std::initializer::list はオブジェクトではなく初期化リストのみを受け取ることができます
使用シナリオ:
#include "SpawnAc.h"
void ASpawnAc::BeginPlay() {
Super::BeginPlay();
SpawningAc({ 1, 2, 3 });//这些1,2,3可以用于actor编号等,如果为字符串可以为actor名字等用途
}
void ASpawnAc::SpawningAc(std::initializer_list<int> llist) {
auto it = llist.begin();
for (; it != llist.end(); it++) {
GetWorld()->SpawnActor<AActor>();
}
}
対応する 3 つのアクターが生成されます
14. for ループの新しい式:
ベクトル<int> a {1, 2, 3};
for(自動アイテム:a){
cout << item << endl;
}
auto item: a は毎回コピーされるため、消費量が大きくなりますが、参照を使用するとコピーが生成されず、項目の値を変更して効率を向上させることができます。
for(auto &item : a) {
cout << item << endl;
}
読み取り専用にしたい場合はconstを追加します
for(const auto &item : a) {
cout << item << endl;
}
15. 呼び出し可能なオブジェクト
#include "iostream"
using namespace std;
using funcptr = void(*)(int, string);
class Test {
public:
Test(int a, string b) {
cout << b << " " << a << endl;
}
operator funcptr() {
return PrintIntAndString;
}
static void PrintIntAndString(int a, string b) {
cout << a << " " << b << endl;
}
};
int main() {
Test t(1, "12");
t(1, "12");
return 0;
}
なぜ static を使わなければならないのかというと、 static の対象はクラスであり、 static のない対象オブジェクトはクラスのオブジェクトだからです 演算子 functptr を使用する場合はオブジェクトがないので、 static を追加する必要があります
クラス オブジェクトをファンクターと呼ばれる関数ポインターに変換します。
16、std::関数
ヘッダー ファイル #include "function" が必要です
std::function<戻り値の型 (関数パラメータ)> name = 呼び出し可能なオブジェクト
1. 通常の関数をラップする
#include "iostream"
#include "functional"
using namespace std;
int hello(string text) {
cout << text << endl;
return 1;
}
int main() {
function<int(string)> testFunc = hello;
hello("123");
return 0;
}
2. 静的関数のパッケージ化
#include "iostream"
#include "functional"
using namespace std;
static void world(string text) {
cout << text << endl;
}
class Test {
public:
static void world(string text) {
cout << text << endl;
}
};
int main() {
function<void(string)> f1 = world;
f1("4322");
f1 = Test::world;
f1("55345");
return 0;
}
#include "iostream"
#include "functional"
using namespace std;
template<typename T, typename T1, typename T2>
class Test {
public:
Test(const function<T(T1, T2)>& f1) : funcptr(f1) {}
void notify(T1 t1, T2 t2) {
funcptr(t1, t2);
}
private:
function<T(T1, T2)> funcptr;
};
void Hello(int a, string b) {
cout << a << " " << b << endl;
}
int main() {
Test<void, int, string> test(Hello);
test.notify(1, "123");
return 0;
}
17、std::バインド
#include "iostream"
#include "functional"
using namespace std;
int Add(int x, int y, const function<void(int, int)>& f1){
if ((x + y) % 2) {
f1(x, y);
}
return 1;
}
void add(int x, int y) {
cout << x + y << endl;
}
int main() {
auto f2 = bind(add, placeholders::_1, placeholders::_2);
Add(2, 5, f2);
Add(3, 5, f2);
return 0;
}
18. ラムダ式
形式: []() {} ->ret{body;};
Lambda 関数を呼び出すには、最後に () とパラメーターを追加する必要があります
ラムダは関数です
19. 右辺値参照
使用シナリオ: A a = 一時オブジェクトの場合; 一時オブジェクトは多数の計算を通じて構築されます。このとき、一時オブジェクトは a にコピーされますが、これも多くのリソースを消費します。ここでは、右辺値参照が大幅に使用されます。オーバーヘッドを削減します。
A &&a = 一時オブジェクト; すぐにコピーして破棄するオーバーヘッドを軽減します
上記の効果を実現するには、まず移動コピーを実現し、一時変数を入力 a として渡し、内部で使用されるポインターのアドレスを新しいオブジェクト内のポインターのアドレスに指定してから、一時変数をnullptrに渡す、これがMove構築、コピー構築や即時破棄を行わずに全ての値を引き継ぐことができるメソッドです
#include "iostream"
using namespace std;
class RightVTest {
public:
RightVTest(int* nnum) : num(nnum) {}
RightVTest(RightVTest&& r) : num(r.num) {
r.num = nullptr;
cout << *num << endl;
}
int* num;
};
RightVTest GetTemp() {
int b = 5;
RightVTest test(&b);
return test;
}
int main() {
int b = 45;
int* num = &b;
RightVTest&& rightVTest = GetTemp();
return 0;
}
auto && a = 左辺値か右辺値か、auto&& a = 5; の場合、auto は int として推定されます; auto&& a = b; の場合、auto は int& として int&&& a = b: 参照の折りたたみ、2 つの参照は次のように推定されます。 int& a = b; として省略されます。
20、std::移動
左辺値を右辺値に変換する
この書き方によれば、左辺値 t2 が右辺値参照 t3 に値を割り当てる方法はありません。
move を追加した後、t2 を左辺値から右辺値に変更すると、t3 に値を割り当てることができます。
t2 が左辺値である場合でも、t2 が左辺値であることを保証するために、move を追加すると左辺値が左辺値になり、右辺値が左辺値になることもあります。いずれにしても、move により変数が左辺値であることが保証されます。
21. std::forward (完全転送)
機能: 右辺値参照の転送中に参照型が変更されないようにするには
ルール(使い方):
フレンド関数はクラスのメンバー関数ではありませんが、クラスのプライベート メンバーにアクセスできます。