概述
1、全文检索工具:Windows Grep。
便于根据关键字查找标准库的源代码。
2、确认自己编译器支持C++11。
#define _cplusplus 201103L
std::cout << _cplusplus;
#if _cplusplus >= 201103L
...
#else
...
#endif
3、使用数量不定的模板参数时,要注意递归的边界。
void print()
{
}
template<typename T, typename... Types>
void print(const T& firstArg, const Type&... args)
{
// sizeof...(args) 打印参数包的参数个数
cout << firstArg << endl;
print(args...);
}
可变参数模板也可以用于递归继承,tuple就是这样实现的。
4、nullptr的类型是std::nullptr_t。
5、大括号的初始化。编译器看到{t1,t2,…,tn}便做出一个initializer_list,它关联至一个array<T,n>。调用函数(例如构造函数)时,该array内的元素可被编译器分解逐一传给函数,但若函数参数是个initializer_list,调用者却不能给予数个T参数然后以为它们会被自动转为一个initializer_list传入。
vector有一个构造函数是接受initializer_list的。其实所有容器都有这种构造函数。.includec++bitsstl_vector.h有如下一段代码:
vector(initializer_list<value_type> __l,
const allocator_type& __a = allocator_type())
: _Base(__a)
{
_M_range_initialize(__l.begin(), __l.end(),
random_access_iterator_tag());
}
6、设置默认的初值:
int n1; // undefined value
int n2{}; // 0
int *p1{}; // nullptr
int x1 = 1.2; // ok
int x2(1.2); // ok
int x3{1.2}; // 警告
7、initializer_list的拷贝是浅拷贝,它的构造函数只是传了一个array的头部指针和一个array的长度。
8、explicit用在一个以上实参的构造函数,避免隐式转换。
- 在 C++ 11 之前,explicit 是用在一个实参的构造函数,因为在 C++ 11 之前没有 initializer_list 作一致性的初始化,在 C++ 11 之前只有一个实参的构造函数才能被隐式转换。
9、range-based for statement
for(int i : {1,2,3})
{
...
}
for(auto elem : vec)
{
...
}
10、关联式容器都不可以改变元素的值,比如set、map等。
11、如果你自己定义了一个ctor,那么编译器就不会再给你一个default ctor。如果你强制加上=default,就可以重新获得并使用default ctor。
构造函数可以有多个,但是拷贝构造函数只能有一个版本。拷贝赋值函数也是只能有一个版本。
一般的成员函数可以修饰为=delete,但是不可以修饰为=default。
=delete可以用于任何函数身上。
=0只能用于virtual函数。
12、别名模板。可用于模板的模板参数。
template <typename T>
using Vec = std::vector<T, MyAlloc<T>>;
Vec<int> v;
#define是无法达到上面的效果的。
typedef也无法达到上面的效果,因为typedef是不接受参数的。
不能对别名模板进行特化。
别名模板不能在函数体内部声明。
13、别名类型,类似于typedef。
下面两句是相等的:
typedef void (*func)(int, int);
using func = void (*)(int, int);
14、可以在类的成员中进行using声明。例如,
class A
{
protected:
using std::cout;
using std::cin;
};
15、满足后面的条件的时候,该函数func不会抛出异常。
void func() noexcept;
即void func() noexcept(true);
16、自定义的class如果有move ctor和move assignment的时候,这两个函数不要抛出异常。因为如果这个类被存放在vector的时候,万一移动构造函数和移动赋值函数抛出了异常,vector不知道如何处理。
17、override保证复写的函数与父类被复写的虚函数的函数签名一致。
18、final用在类不想被继承的时候,或者虚函数不想被子类复写的时候。
class A final {};
class B
{
virtual void func() final;
};
19、decltype关键字。
map<string, float> coll;
decltype(coll)::value_type elem;
在C++11之前是:
map<string, float>::value_type elem;
20、尾置返回类型。和lambdas类似。
template<typename T1, typename T2>
auto add(T1 x, T2 y) -> decltype(x+y);
其实含义上就是
template<typename T1, typename T2>
decltype(x+y) add(T1 x, T2 y); // 但语法不是这样写的,因为编译器在编译到decltype的时候不知道x和y是什么.
21、面对lambda,我们手上往往只有object,没有type。
要获得其type,就得借助于decltype。
auto cmp = [](const Person &p1, const Person &p2) { ... };
std::set<Person, decltype(cmp)> coll(cmp);
22、lambda表达式。lambda表达式其实是一个inline函数。
[] { … }; // 这里只是定义,但不会调用
[] { … }(); // 这里是直接调用
但我们一般这样用:
auto func = [] { ... };
func();
如果没有mutable就不能++id。
int id = 0;
auto f = [id]() mutable {
++id;
};
类似于以下函数对象:
class Func
{
public:
void operator() { ++id; }
private:
int id;
};
Func f;
lambda的内部实现有点像函数对象,但是它是没有默认构造函数和赋值运算符的。
23、可变参数模板,变化的是参数个数和参数类型。
-
利用参数个数逐一递减的特性,实现递归函数调用.使用函数模板完成。
-
利用参数个数逐一递减导致参数类型也逐一递减的特性,实现递归继承或递归复合,以类模板完成。
-
递归调用处理的都是参数,使用函数模板。
-
递归继承处理的是类型,使用类模板。
24、printf可以用可变参数模板模拟实现。
template<typename T, typename... Args>
void printf(cosnt char* s, T value, Args... args)
{
while(*s)
{
if(*s == '%' && *(++s) != '%')
{
std::cout << value;
printf(++s, args...);
return;
}
std::cout << *s++;
}
throw std::logic_error("extra arguments provided to printf");
}
void printf(cosnt char* s)
{
while(*s)
{
if(*s == '%' && *(++s) != '%')
throw std::runtime_error("invalid format string: missing arguments");
std::cout << *s++;
}
}
25、利用可变参数模板实现 cout << maximum(5,2,0,1,3,1,4); 的功能。其实也可以用 cout << std::max( {5,2,0,1,3,1,4} );
int maximum(int n)
{
return n;
}
template<typename... Args>
int maximum(int n, Args... args)
{
return std::max(n, maximum(args...));
}
26、public继承是is a的关系。private继承不是,例如tuple的实现。
- private继承与复合类的作用类似。因此,tuple也可以用复合来实现。
27、左值可以出现于operator=左侧。右值只能出现于operator=右侧。
临时对象就是一种右值。
int func() { return 5; }
int* p = &func(); // 错误写法。不能对右值取其引用。
func() = 7; // 错误写法。
通过std::move()函数可以从左值得到右值。
M c1(c);
M c2(std::move(c1));
28、右值经由一个函数A传给另一函数B是会变为左值的。
因此需要完美转发。
void B(int& i)
{
}
void B(int&& i)
{
}
void A(int&& i)
{
// B(i); // 调用的是void B(int& i)。
B(std::forward<int>(i)); // 完美转发
}
A(5);
29、移动构造函数和移动赋值函数要把传入对象的成员变量的指针置为nullptr,并且在析构函数的时候要判断-不为空的时候进行delete。
30、存放在容器里面的东西,如果这个容器是以节点的形式存放的(例如list、set等,vector和deque不是以节点存放的),那么这个东西(对象的类)有没有移动构造函数和移动赋值函数并不重要,它们性能差异不大。vector和deque存在内存的扩张,会有大量元素的拷贝,因此存在在它俩里面的对象的类就有必要有移动构造函数和移动赋值函数。
(侯老板说得不对吧?!)
但是如果是容器(对整个容器的操作),容器就必须要有移动构造函数和移动赋值函数。
31、
int a[100];
// int[100] b; // 错误写法。
typedef int T[100];
T c;
最后
以上就是热心小蚂蚁为你收集整理的【C++】04-C++新标准C++11&14-笔记(侯捷系列)的全部内容,希望文章能够帮你解决【C++】04-C++新标准C++11&14-笔记(侯捷系列)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复