系列文章目录
文章目录
前言
一、fast view
列表初始化 std::initializer_list
{初始化列表},可用于所有的内置类型和用户定义的类型,可添加=也可不加。
eg:
int x = {5}; // = {3.4} 报错
double y = {2.35};
int *ar = new int[4] {2,3,4,5};
class UserDef
{
private:
int roots;
double w;
public:
UserDef(int r, double ww): roots(r), w(w) {}
};
UserDef s1(1, 3.42);
UserDef s2{1, 3.45};
UserDef s2 = {1, 3.45};
如果有类将模板std::initializer_list作为参数的构造函数,则只有该构造函数可以使用列表初始化形式
STL容器提供了将initializer_list作为参数的构造函数
模板类initializer_list类包含begin(),end(), size()方法
#include <initializer_list>
template<class T>
T sum(std::initializer_list<T> il)
{
T tot = 0;
for (auto ite = il.begin(); ite != il.end(); ++ite){
tot += *ite;
}
return tot;
}
int main()
{
double total = sum({2.5, 3.1, 5.7});
}
auto
auto: http://c.biancheng.net/view/3718.html
int x = 0;
auto * a = &x; // a -> int*,auto被推导为int
auto b = &x; // b -> int*,auto被推导为int*
auto & c = x; // c -> int&,auto被推导为int
auto d = c; // d -> int ,auto被推导为int
const auto e = x; // e -> const int
auto f = e; // f -> int
const auto& g = x; // g -> const int&
auto& h = g; // h -> const int&
由上面的例子可以看出:
- a 和 c 的推导结果是很显然的,auto 在编译时被替换为 int,因此 a 和 c 分别被推导为 int* 和 int&。
- b 的推导结果说明,其实 auto 不声明为指针,也可以推导出指针类型。
- d 的推导结果说明当表达式是一个引用类型时,auto 会把引用类型抛弃,直接推导成原始类型 int。
- e 的推导结果说明,const auto 会在编译时被替换为 const int。
- f 的推导结果说明,当表达式带有 const(实际上 volatile 也会得到同样的结果)属性时,auto 会把 const 属性抛弃掉,推导成 non-const 类型 int。
- g、h 的推导说明,当 auto 和引用(换成指针在这里也将得到同样的结果)结合时,auto 的推导将保留表达式的 const 属性。
通过上面的一系列示例,可以得到下面这两条规则:
- 当不声明为指针或引用时,auto 的推导结果和初始化表达式抛弃引用和 cv 限定符后类型一致。
- 当声明为指针或引用时,auto 的推导结果将保持初始化表达式的 cv 属性。
注意 auto 是一个很强大的工具,但任何工具都有它的两面性。不加选择地随意使用 auto,会带来代码可读性和维护性的严重下降。因此,在使用 auto 的时候,一定要权衡好它带来的“价值”和相应的“损失”。
decltype
decltype的工作原理比auto负载,根据使用的表达式,指定的类型可以为引用和const
int j = 3;
int &k = j
const int &n = j;
decltype(n) i1; // i1 type const int& 比auto更强大
decltype(j) i2; // int
decltype( (j) ) i3; // int&
decltype(k+1) i4; //int decltype并不会实际计算表达式的值,编译器分析表达式并得到它的类型
using
using itType = std::vector<std::string>::iterator; 同typedef
typedef 不行
template<typename T>
using arr12 = std::array<T, 12>; // template for multiple aliases
智能指针
https://blog.csdn.net/surfaceyan/article/details/124449692
移动语义与右值引用
int ival = 4;
int&& ref = 5; // 右值变左值
int&& refi= std::move(ival); // 左值变右值
移动语义,移动构造函数
Useless是一个string类,记录pc指针和n
还有一种只需要pc指针即可实现string的方式:
#include <iostream>
using namespace std;
class Useless
{
private:
int n;
char* pc;
static int ct;
void ShowObject() const;
public:
Useless();
explicit Useless(int k);
Useless(int k, char ch);
Useless(const Useless& f);
Useless(Useless && f);
Useless operator+(const Useless& f) const;
Useless& operator=(const Useless& f);
Useless& operator=(Useless&& f);
~Useless();
void ShowData() const;
};
// implementation
int Useless::ct = 0;
Useless::Useless()
{
++ct;
n = 0;
pc = nullptr;
cout << "default constructor called; number of objects: " << ct << endl;
ShowObject();
}
Useless::Useless(int k): n(k)
{
++ct;
cout << "int constructor called; number of objects: " << ct << endl;
pc = new char[n];
ShowObject();
}
Useless::Useless(int k, char ch): n(k)
{
++ct;
cout << "int, char constructor called; number of objects: " << ct << endl;
pc = new char[n];
for (int i = 0; i < n; ++i) {
pc[i] = ch;
}
ShowObject();
}
Useless::Useless(const Useless& f): n(f.n)
{
++ct;
cout << "copy const called; number of objects: " << ct << endl;
pc = new char[n];
for (int i=0; i < n; ++i) {
pc[i] = f.pc[i];
}
ShowObject();
}
Useless::Useless(Useless&& f): n(f.n)
{
++ct;
cout << "move constructor called; number of objects: " << ct << endl;
pc = f.pc;
f.pc = nullptr;
f.n = 0;
ShowObject();
}
Useless& Useless::operator=(const Useless& f)
{
if (this == &f) {
return *this;
}
delete[] pc;
n = f.n;
pc = new char[n];
memcpy(pc, f.pc, n); // string.h
return *this;
}
Useless& Useless::operator=(Useless&& f)
{
if (this == &f) {
return *this;
}
delete[] pc;
pc = f.pc;
n = f.n;
f.pc = nullptr;
f.n = 0;
return *this;
}
Useless::~Useless()
{
cout << "destructor called; objects left: " << --ct << endl;
cout << "deleted object:\n";
ShowObject();
delete[] pc;
}
Useless Useless::operator+(const Useless& f) const
{
cout << "Entering operator+()\n";
Useless tmp = Useless(n + f.n);
for (int i=0; i<n; ++i) {
tmp.pc[i] = pc[i];
}
for (int i=n; i<tmp.n; ++i) {
tmp.pc[i] = f.pc[i-n];
}
cout << "tmp object:\n" << "Leaving operator+()\n";
return tmp;
}
void Useless::ShowObject() const
{
cout << "Number of elements: " << n;
cout << " Data address: " << (void*)pc << endl;
}
void Useless::ShowData() const
{
if (n == 0) {
cout << "(object empty)";
} else {
for (int i=0; i<n; ++i) {
cout << pc[i];
}
}
cout << endl;
}
int main()
{
{
Useless one(10, 'x');
Useless two = one;
Useless three(20, 'o');
Useless four(one + three);
cout << "object one: ";
one.ShowData();
cout << "object two: ";
two.ShowData();
cout << "object three: ";
three.ShowData();
cout << "object four: ";
four.ShowData();
}
}
在linux下用g++进行编译得到的结果过:
实测没有调用移动构造,且只创建了4个对象。创建four时,该编译器没有调用任何构造函数:相反,它推断出对象four是operator+()所做工作的受益人,因此将operator+()创建的对象转移到four的名下。
实测将移动构造函数注释掉后程序输出结果不变
int, char constructor called; number of objects: 1
Number of elements: 10 Data address: 0x55e7d2a262c0
copy const called; number of objects: 2
Number of elements: 10 Data address: 0x55e7d2a262e0
int, char constructor called; number of objects: 3
Number of elements: 20 Data address: 0x55e7d2a26300
Entering operator+()
int constructor called; number of objects: 4
Number of elements: 30 Data address: 0x55e7d2a26320
tmp object:
Leaving operator+()
object one: xxxxxxxxxx
object two: xxxxxxxxxx
object three: oooooooooooooooooooo
object four: xxxxxxxxxxoooooooooooooooooooo
destructor called; objects left: 3
deleted object:
Number of elements: 30 Data address: 0x55e7d2a26320
destructor called; objects left: 2
deleted object:
Number of elements: 20 Data address: 0x55e7d2a26300
destructor called; objects left: 1
deleted object:
Number of elements: 10 Data address: 0x55e7d2a262e0
destructor called; objects left: 0
deleted object:
Number of elements: 10 Data address: 0x55e7d2a262c0
没有移动语义的老式编译器:
首先在operator+()内,调用构造函数创建tmp,并在01C337C4处给它分配了存储30个元素的空间。然后,调用拷贝构造创建临时对象(地址为01C337E8),f指向该副本。接下来,删除地址为01C337C4的对象tmp。然后,新建了对象four,它使用了01C337C4处刚刚释放的内存。接下来,删除了01C337E8处的临时参数对象。这表明,总共创建了三个对象,但其中的两个被删除。移动语义旨在消除这些额外的复制。
std::move强制移动
Useless one;
Useless two;
two = std::move(one); // 将调用 Useless& operator=(Useless&&);
包装器
// ef代表可调用类型,如lambda,函数,重载了operator()()的对象
answer = ef(q);
template <class T, class T>
T use_f(T v, F f)
{
return f(v);
}
#include <functional>
using fdd = std::function<double(double)>;
use_f(y, fdd(square));
use_f(y, fdd( Fq(3) ) );
...
template<class T>
T use_f(T v, std::function<T(T)> f)
{
static int count = 0;
count++;
return f(v);
}
use_f<double>(y, square);
use_f<double>(y, Fq(3) );
...
参数dub、Fp(5.0)等本身的类型并不是function<double(double)>,因此在use_f后面使用了<double>来指出所需的具体化。T被设置为double,而std::function<T(T)>变成了std::function<double(double)>。
可变参数模板
几个要点:
- 模板参数包(parameter pack)
- 函数参数包
- 展开(unpack)参数包
- 递归
// definition for 0 parameter
void show_list() {}
// definition for 1 parameter
template<typename T>
void show_list(const T& value)
{
std::cout << value << "\n";
}
// definition for 2 or more parameters
template<typename T, typename ... Args>
void show_list(const T&value, const Args&... args)
{
std::cout << value << ", ";
show_list(args...);
}
chrono
using std::chrono::steady_clock;
chrono中分steady_clock和system_clock类
二、其他
线程库
regex
[ABcd]匹配单个字符
\d匹配单个数字
R"(字面值字符)"
R"任意符号(字面值字符)任意符号"
alignof() 运算符
说明符 alignas
constexpr
为嵌入式提供的便利
bitset
防御性编程
assert
static_assert
元编程指的是编写这样的程序,它创建或修改其他程序,甚至修改自身。
成员解除引用运算符
alignof
计算机系统可能限制数据在内存中的存储方式。例如,一个系统可能要求double值存储在编号为偶数的内存单元中,而另一个系统要求其起始地址为8个整数倍。运算符alignof将类型作为参数,并返回一个整数,指出要求的对齐方式。
noexcept
c++17
any
filesystem
atomic
总结
建议:
对内置类型以及stl使用 列表初始化 std::initializer_list
使用类内初始化