C++ 11 new features study notes

1. String primitive literal

R "()" is used for unescaping and can be used for path representation

run successfully

These two RawValues ​​play a descriptive role (can not be written), and do not participate in the output

Note that Chinese garbled characters are output here 


2、nullptr

NULL means 0 in C++, and universal pointer in non-C++

nullptr is a null pointer, not 0, compatible with various types of pointers, but empty


3、constexpr

When defining constants, const and constexpr are equivalent

When decorating a function with constexpr, the function should be as compact as possible:

1. Statements other than non-constant expressions cannot appear (except for using directives, typedef directives, static_assert, and return statements)

For example, there cannot be a for loop

Scenario: simple calculation


 4. Auto and decltype type deduction

auto cannot define arrays:

auto a[10] = { 1, 2, 3 };//Wrong way of writing 

The object deduced by auto must be declared and assigned at the same time, for example, auto a = 3; but not directly auto a; because the type of a is deduced through this 3, and decltype does not need

In 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);
}

The compilation is successful, and the running effect is as follows:

When T has no constructor, the return value of the function needs to be rearranged: here we take TArray<int> as an example

auto Function->decltype(TArray<int>) {}


5. Functions modified by final cannot be rewritten or inherited by virtual functions. The correct usage is as follows:

At this time, after inheriting from Child, the test method cannot be rewritten. As the saying goes, final cuts off children and grandchildren, um, easy to remember

Add final after the Child class, after which the class cannot be inherited


6、override

If class B inherits from class A, and type B wants to define a method that is exactly the same as class A, if the method of class A has virtual decoration, then the behavior is rewriting, if there is no virtual decoration, it is called overriding, if it is only the meaning of the method name , and the rest are different, it is called overloading


7. Angle brackets

For example, in the original TArray<list<int>> a, the >> here will be ambiguous, and the operation symbol of 4>>2 must be distinguished by adding a space to TArray<list<int>> a before, but after c++11, it will be It can be written as TArray<list<int>> a


8. Template default type

Not only can the type be determined by passing the type, but also the object parameter can be passed for type deduction, as above


9. using function

① Namespace

Usage scenario: In C/C++, there are a large number of variables, functions, and classes. The names of these variables, functions, and classes will all exist in the global scope, which may cause many conflicts . The purpose of using namespaces is to localize the names of identifiers to avoid naming conflicts or name pollution.

using namespace std;

② Define type aliases (and typedef meaning)

using MY_INT = int ;

typedef int MY_INT;

Summary: using looks more intuitive

// 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. Delegating constructor

Since the second constructor contains the first constructor, it can be written as

Delegated construction is the use of constructors nested in other constructors


11. Inherited constructor

It is equivalent to writing the following code:

Improve the speed of writing code, because Child inherits Base's int m_i, double m_j, string m_k, so these variables must also be initialized in Child, so you have to write the assignment of their parent class constructor, which is extremely Trouble, now you can directly replace using Base::Base, and it’s over, this is the inheritance constructor


12. Initialization list


13、std::initializer::list

Ability to receive any number of identical arguments

#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 can only receive initialization lists, not objects

Usage scenario:

#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>();
	}
}

 The corresponding 3 actors are generated


14. The new expression of the for loop:

vector<int> a {1, 2, 3};

for(auto item : a){

        cout << item << endl;

}

Because auto item: a will be copied every time, and the consumption will be larger. If you use a reference, no copy will be generated, and the value of the item can be modified to improve efficiency:

for(auto &item : a) {

        cout << item << endl;

}

If you want to read only, add a const

for(const auto &item : a) {

        cout << item << endl;

}


15. Callable objects

#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;
}

Why do you have to use static, because the target of static is a class, and the target object without static is an object of a class. There is no object when operator functptr is used, so you must add static

Convert a class object into a function pointer, which is called a functor


16、std::function

Requires header file #include "functional"

std::function<return type (function parameter)> name = callable object

1. Wrap ordinary functions

#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. Packaging static functions

#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::bind

#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. Lambda expression

Format: []() {} ->ret{body;};

 

To call the Lambda function, you must add () and parameters at the end 

lambda is a functor


19. Rvalue references

Usage scenario: when A a = temporary object; the temporary object is constructed through a large number of calculations. At this time, the temporary object is copied to a, which also consumes a lot of resources. Here, rvalue references are used to greatly reduce the overhead:

A &&a = temporary object; reduces the overhead of copying and destroying immediately

To achieve the above effect, you must first realize the moving copy, pass the temporary variable as input a, give the address of the pointer to be used internally to the address of the pointer in the new object, and then assign the pointer of the temporary variable to nullptr, this is Move construction, a method that can take all the values ​​​​over without copy construction or immediate destruction

#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 = Whether it is an lvalue or an rvalue, if auto&& a = 5; then auto is deduced as int; if auto&& a = b; then auto is deduced as int& as int&&& a = b: reference folding, two references are omitted as int& a = b;


20、std::move

convert lvalue to rvalue 

According to this way of writing, there is no way for the lvalue t2 to assign a value to the rvalue reference t3

After adding move, change t2 from an lvalue to an rvalue, and you can assign a value to t3

Even if t2 is an lvalue, in order to ensure that t2 is an lvalue, adding move can also make the lvalue become an lvalue, and the rvalue become an lvalue. Anyway, move ensures that the variable is an lvalue 


21. std::forward (perfect forwarding)

Function: To ensure that the reference type does not change during the transfer of rvalue references

Rules (usage):


A friend function is not a member function of a class, but it can access private members of the class 

Guess you like

Origin blog.csdn.net/qqQQqsadfj/article/details/132314755