C++ 11 新特性 学习笔记

1、字符串原始字面量

R“()”用于取消转义,可用于路径表示

运行成功

这两个RawValue起到描述作用(可以不写),并不参与输出

注意,这里输出中文乱码 


2、nullptr

NULL在C++中表示0,在非C++中表示万能指针

nullptr是空指针,而不是0,能兼容各种类型的指针,但为空


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;因为是通过这个3来推导a的类型的,而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过后就不能对test方法进行重写了,俗话说的好,final断子绝孙,嗯,好记

在Child类后面加final,之后类就不能被继承了


6、override

如果B类继承自A类,B类型想定义一个与A类一模一样的方法,若A类的该方法有virtual修饰,那么该行为为重写,若没有virtual修饰,叫覆盖,若只是方法名意义,其余不一样,则叫重载


7、尖括号

例如原来TArray<list<int>> a,这里的>>会有歧义,4>>2的运算符号,之前必须TArray<list<int> > a加个空格来区别,而c++11过后就可以写成TArray<list<int>> a了


8、模板默认类型

不只是可以通过传递类型进行类型确定,也可以传递对象参数进行类型推导,如上


9、using功能

①命名空间

使用场景:在C/C++中,变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。

using namespace std;

②定义类型别名 (和typedef意义)

using MY_INT = int ;

typedef int MY_INT;

总结:using看起来更直观

// 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、委托构造函数

由于第二个构造函数包含第一个构造函数,所以可以写成以下形式

委托构造就是构造函数嵌套其它构造函数进行使用


11、继承构造函数

相当于写了以下代码:

提高了写代码的速度,因为Child继承了Base的int m_i,double m_j, string m_k,所以要在Child中也要对这些变量进行初始化,所以就得每个写它们父类构造函数的赋值,极其麻烦,现在可以直接替换称using 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个actor


14、for循环的新式表达:

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

for(auto item : a){

        cout << item << endl;

}

因为auto item : a每次都会拷贝,消耗会大一些,如果用引用则不会产生拷贝,还能修改item的值,从而提升效率:

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的目标对象是类的对象,在operator functptr的时候是没有对象的,所以一定要加上static

将类对象转化为函数指针,该类函数称作为仿函数


16、std::function

需要头文件#include "functional"

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::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表达式

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

 

要调用Lambda函数一定要在最后加上()以及参数 

lambda是仿函数


19、右值引用

使用场景:当A a = 临时对象;该临时对象通过大量运算进行构造出来,这时候,临时对象再拷贝给a,这样同样也会消耗大量资源,这里就用右值引用大大减少了开销:

A &&a = 临时对象; 减少了拷贝,且立马销毁的开销

要以上效果必须得先实现移动拷贝,将临时变量当作输入a传进来,将内部要用的指针的地址给到新对象中的指针的地址,然后将临时变量的指针赋值为nullptr,这个就是移动构造,不用拷贝构造,不用立马销毁就可以将值全部拿过来的方法

#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:引用折叠,两个引用就省略为int& a = b;


20、std::move

将左值转化为右值 

按照这样写法,左值t2是没有办法给右值引用t3赋值的

加上move过后将t2从左值变成右值,即可对t3进行赋值

即使t2是左值,为了确保t2是左值,那么加上move也可以让左值变成左值,右值变成左值,反正move确保变量是左值 


21、std::forward(完美转发)

作用:保证右值引用传递过程中,引用类型不发生变化

规则(用法):


友元函数不属于类的成员函数,但是它能够访问类的私有成员 

猜你喜欢

转载自blog.csdn.net/qqQQqsadfj/article/details/132314755
今日推荐