- 1. 演进、环境与资源
- 版本:c++98(1.0),c++04,c++11(2.0),c++14
- 2. Variadic Template(可变参数模板)(重点,也是难点)
- ...就是pack(包),表示任意个数、任意类型的参数
- template parameters pack:模板参数包
- function parameter types pack:函数参数类型包
- function parameter pack:函数参数包
- 模板有特化的概念。重载中,函数谁会更特别,则调用更特化的。
- 代码:
-
#include<iostream>
using namespace std;
namespace variadic{
void print(){
}
template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args){
cout << sizeof...(args) << " " << firstArg << endl;
print(args...);
}
template<typename... Values> class tuple;
template<> class tuple<> {};
template<typename Head, typename... Tail>
class tuple<Head, Tail...>: private tuple<Tail...>{
typedef tuple<Tail...> inherited;
public:
tuple() {}
tuple(Head v, Tail... vtail): m_head(v), inherited(vtail...) {}
Head head() {return m_head;}
inherited& tail() { return *this; }
protected:
Head m_head;
};
}
using namespace variadic; // 如果不加这一行,编译不通过
int main(){
int a = 1;
char c = 'T';
int b[10] = {0};
string s("abs");
// cout << b << endl; //打印的是地址
// s = "dsd"; // 可以通过编译
print(a, c, b, s);
// tuple<int, float, string> t(41, 6.3, "nico");
// cout << t.head() << endl;
// cout << t.tail().head() << endl;
return 0;
}
- 3. Spaces in Template Expression、nullptr、auto
- Spaces in Template Expression
- vector<vector<int> > v; 可以写成 vector<vector<int> > v;(中间的空格可以不写
- nullptr
- C++11开始,定义了空指针的常值为nullptr
- auto
- auto可以自动推导变量的类型,当类型比较长、或者复杂的表达式 lambda,可以使用auto代替。
- 4. Unifrom Initialzation一致性初始化
- 需要注意的是,initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值。并且,拷贝或赋值一个initializer_list对象不会拷贝列表中的元素,其实只是引用而已,原始列表和副本共享元素,本质是由于这是一种浅拷贝,二者底层上只是把一个指针同时指向一个地址。
- 现在有了initializer_list<>,所有容器都接受指定类型任意数量的值用于构建或赋值等(类型要一致,{}不会进行类型转换)。
- 代码:
- vector<int> v {1, 2, 3, 4};
- 5. Initialzer_list
- std::initializer_list有三个成员接口:size(), begin(), end();
- 6. Explicit for ctors taking more than one argument带多个参数的系数的显式
- 加入了explicit, 必须显式调用构造函数。不允许进行隐式的调用构造函数。
- 代码:
-
#include<iostream>
struct Complex{
double _real;
double _i;
// explicit // 加入了explicit, 必须显式调用构造函数
Complex(double real, double i = 0) : _real(real), _i(i) {}
Complex operator + (const Complex &c){
return Complex(c._real + _real, c._i + _i);
}
~Complex() = default;
};
int main() {
Complex c1(4, 2);
Complex c2 = c1 + 5; // 编译器会隐式地调用构造函数来构造一个Complex(5)
return 0;
}
- 7. Range - based for statement
- 代码:
- vector<int> v {1,2, 3,4};
- 8. =default. =delete(https://blog.csdn.net/baidu_17611285/article/details/80505902)
- big-five:默认的构造函数,默认的拷贝构造函数,默认的赋值构造函数,移动构造函数,移动赋值构造函数
- default就是专门作用于以上的big-five,表示当我们已经定义了专门的构造、拷贝构造、赋值构造、移动构造等等函数,此时编译器就不会自动加上big-five,但假如此时还希望用这些默认的函数,可加上=default来实现,如下图。而“=delete”表示的是我们不想要这个函数,若去调用加了“=delete”的函数,则编译报错。另外,“=delete“可加在任意函数,不局限于类的成员函数。default还能用于析构函数身上,但很少用到。
- =default(也可以使用在析构函数上)
- 在标准库中,default都是用于big-five的函数。
- 一个类只要带着指针成员,几乎就断定需要我们自己去写出那些Big-Three(构造,拷贝构造,赋值构造,C++11就是Big-FIve);反之,如果不带指针成员,几乎就不需要自己去实现Big-Three,一般编译器默认的就够了。因为,编译器的默认拷贝是浅拷贝,带指针的就只拷贝指针本身,一般我们需要深拷贝,也就是指针所指的那块内存要重新分配,再拷贝指针。
- ?
#include<iostream>
using namespace std;
class Zoo{
private:
int d1, d2;
public:
Zoo(int i1, int i2):d1(i1), d2(i2) {}
// 由于写了构造函数,系统不会自动在生成构造函数。
// 故如果不写下面这一行,Zoo z1;将会报错。
Zoo() = default;
Zoo(const Zoo&) = delete;
Zoo(Zoo &&) = default;
Zoo& operator = (const Zoo&) = default;
Zoo& operator = (const Zoo&&) = delete;
virtual ~Zoo() {}
};
int main() {
Zoo z1;
// Zoo z2(z1); // 由于使用了Zoo(const Zoo&) = delete;,故这一行报错
return 0;
}
- 9. Alias Template(模板别名)(8.Alias Template(模板别名)_baidu_17611285的博客-CSDN博客_alias template)
- using这种方法类似于C++中的typedef,但这种C++11的新机制不仅仅是为了通过设置模板别名来少写几个字,且其也无法通过typedef或#define代替
-
#include<iostream>
#include<vector>
using namespace std;
// template要放在函数外面
// 使用typedef语法复制等
template<typename T>
using Vec = vector<T, allocator<T>>;
template <typename T, template <class> class Container>
class XCLs {
private:
Container<T> c;
public:
XCLs() {
for(long i = 0; i < 10; i++)
c.insert(c.end(), T());
// output_static_data(T());
Container<T> c1(c);
// std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.。
Container<T> c2(std::move(c));
c1.swap(c2);
}
};
int main() {
XCLs<int, Vec> c1;
return 0;
}
- 10.Template template parameter
- 11. Type Alias, noexception, override, final(C++新特性之Type Alias、noexcept、override、final_u013635579的博客-CSDN博客)
- override
- 在C++11中提供的override关键字可以解决这一问题,它标记派生类中的虚函数,如果基类中的虚函数没有被覆盖,编译器则报错
- final
- final新增两种功能:(1)、禁止基类被继承,(2)、禁止虚函数被重写;
- 12. decltype
- decltype(表达式),返回表达式(可以使函数)的类型。
- dectype并不会执行表达式,只是推出类型,而auto会执行。
- 13. lambdas(C++之Lambda表达式 - 季末的天堂 - 博客园)
- [函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
- C++ 11 中的 Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。
- =。函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是值传递方式(相
- &。函数体内可以使用 Lambda 所在范围内所有可见的局部变量(包括 Lambda 所在类的 this),并且是引用传递方式
- (相当于是编译器自动为我们按引用传递了所有局部变量)。
- this。函数体内可以使用 Lambda 所在类中的成员变量。
- a。将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是 const 的,要
- 修改传递进来的拷贝,可以添加 mutable 修饰符。
- =,&a,&b。除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
- &,a,b。除 a 和 b 按值进行传递外,其他参数都按引用进行传递。
- 14. Rvalue reference and Move Semantics
- lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。
- rvalue通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是不在内存中占有确定位置的表达式。
- 举例:理解C和C++中的左值和右值_xuwqiang1994的博客-CSDN博客
- 常量4和表达式var+1都不是lvalue(它们是rvalue)。它们不是lvalue,因为都是表达式的临时结果,没有确定的内存空间(换句话说,它们只是计算的周期驻留在临时的寄存器中)。因此给它们赋值没有语意-这里没有地方给它们赋值。
- foo返回一个临时的rvalue。尝试给它赋值,foo()=2,是一个错误;编译器期待在赋值运算符的左部分看到一个lvalue。
- 不是所有的对函数调用结果赋值都是无效的。比如,C++的引用(reference)让这成为可能:
- const int a = 10; //‘a’是一个左值
- […] 一个左值没有数组类型,没有不完全类型,没有const修饰的类型,并且如果它是结构体或联合体,则没有任何const修饰的成员(包含,递归包含,任何成员元素的集合)。
- 一个非函数,非数组类型的左值T可以被转换成一个右值。[…]如果T是一个非类类型,那么转换成的右值类型是T的非CV修饰版本。否则,那个右值类型是T。
- && 语法是新的右值引用。的确如它名字一样-给我们一个右值的引用,在调用之后将被析构。我们可以使用我们只是“偷”这个内部的右值这个事实-我们根本不需要它们!输出为:
- 代码:
-
#include<iostream>
using namespace std;
class Intvec
{
public:
explicit Intvec(size_t num = 0)
: m_size(num), m_data(new int[m_size])
{
log("constructor");
}
~Intvec()
{
log("destructor");
if (m_data) {
delete[] m_data;
m_data = 0;
}
}
Intvec(const Intvec& other)
: m_size(other.m_size), m_data(new int[m_size])
{
log("copy constructor");
for (size_t i = 0; i < m_size; ++i)
m_data[i] = other.m_data[i];
}
// Intvec& operator=(const Intvec&& other) // 执行v2 = v1;会报错
Intvec& operator=(const Intvec& other)
{
log("copy assignment operator");
Intvec tmp(other); // 函数执行完后, tmp会被释放掉
// other中的内容还在
std::swap(m_size, tmp.m_size); //由于other是常量类型,故不能调用std::swap(m_size, other.m_size);
std::swap(m_data, tmp.m_data);
return *this;
}
Intvec& operator=(Intvec&& other) // 将&&理解成偷
{
log("move assignment operator");
std::swap(m_size, other.m_size);
std::swap(m_data, other.m_data);
return *this;
}
private:
void log(const char* msg)
{
cout << "[" << this << "] " << msg << "\n";
}
size_t m_size;
int* m_data;
};
int main() {
Intvec v1(20);
Intvec v2;
cout << "assigning lvalue...\n";
// v2 = v1;
// Intvec(33)构建了一个临时对象,执行完下面这一条语句,会被释放。
// Intvec& operator=(const Intvec& other), 里面要是const类型,不然如果不在写一个=重载函数,系统会说找不到=重载函数
// 或者写成 Intvec& operator=(Intvec&& other)
v2 = Intvec(33); //系统调用的是Intvec& operator=(Intvec&& other)
cout << "ended assigning lvalue...\n";
return 0;
}
- 15. Perfect Forwarding(完美传递)
- 顾名思义,完美转发是将参数不改变属性的条件下,转发给下一个函数. 因为普通函数的参数一旦具名,始终都是lvalue. 如果把rvalue转发到下一个函数上的参数中,还是rvalue(不会产生过多的内存)这就是完美转发的目的。
- 16. Move-aware class
- 17. Vector
- 18. Hashtable, Hast funciton
- Hast funciton(系统会有一个计算hashcode的函数)
- 19. Tuple
- 参看2. Variadic Template代码。
|