C++11 是第二个真正意义上的 C++ 标准,也是 C++ 的一次重大升级。C++11增加了很多现代编程语言的特性,比如自动类型推导、智能指针、lambda 表达式等,这使得 C++ 看起来又酷又潮,一点也不输 Java 和C#。
GitHub地址:https://github.com/0voice/cpp_new_features#cpp_11 
Linuxc/c++后台开发方向技术文章资料电子书整理
2021-2000道大厂面经/面试题整理(腾讯、字节、阿里、百度、京东等)
关键字
新增关键字
thread_local
thread_local是C++11增加的存储类指定符
C++中有4种存储周期:
- automatic
- static
- dynamic
- thread
有且只有thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例(Each thread has its own instance of the object)。thread_local 可以和static 与 extern关键字联合使用,这将影响变量的链接属性(to adjust linkage)。
那么,哪些变量可以被声明为thread_local?可以是以下3类:
- 命名空间下的全局变量
- 类的static成员变量
- 本地变量
thread_local案例
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
thread_local unsigned int rage = 1;
std::mutex cout_mutex;
void increase_rage(const std::string& thread_name) {
++rage; // 在锁外修改 OK ;这是线程局域变量
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}
void test() {
thread_local int i = 0;
printf("id=%d, n=%d\n", std::this_thread::get_id(), i);
i++;
}
void test2() {
test();
test();
}
int main() {
std::thread a(increase_rage, "a"), b(increase_rage, "b");
{
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << "Rage counter for main: " << rage << '\n';
}
a.join();
b.join();
std::thread t1(test);
std::thread t2(test);
t1.join();
t2.join();
std::thread t3(test2);
t3.join();
system("pause");
return 0;
}
static_assert
static_assert
struct MyClass
{
char m_value;
};
struct MyEmptyClass
{
void func();
};
// 确保MyEmptyClass是一个空类(没有任何非静态成员变量,也没有虚函数)
static_assert(std::is_empty<MyEmptyClass>::value, "empty class needed");
//确保MyClass是一个非空类
static_assert(!std::is_empty<MyClass>::value, "non-empty class needed");
template <typename T, typename U, typename V>
class MyTemplate
{
// 确保模板参数T是一个非空类
static_assert(
!std::is_empty<T>::value,
"T should be n non-empty class"
);
// 确保模板参数U是一个空类
static_assert(
std::is_empty<U>::value,
"U should be an empty class"
);
// 确保模板参数V是从std::allocator<T>直接或间接派生而来,
// 或者V就是std::allocator<T>
static_assert(
std::is_base_of<std::allocator<T>, V>::value,
"V should inherit from std::allocator<T>"
);
};
// 仅当模板实例化时,MyTemplate里面的那三个static_assert才会真正被演算,
// 藉此检查模板参数是否符合期望
template class MyTemplate<MyClass, MyEmptyClass, std::allocator<MyClass>>;
nullptr
nullptr nullptr关键字用于标识空指针,是std::nullptr_t类型的(constexpr)变量。它可以转换成任何指针类型和bool布尔类型(主要是为了兼容普通指针可以作为条件判断语句的写法),但是不能被转换为整数。
char *p1 = nullptr; // 正确
int *p2 = nullptr; // 正确
bool b = nullptr; // 正确. if(b)判断为false
int a = nullptr; // error
noexcept
noexcept noexcept有两类作用:noexcept指定符和noexcept运算符
void f() noexcept; // 函数 f() 不抛出
void (*fp)() noexcept(false); // fp 指向可能抛出的函数
void g(void pfa() noexcept); // g 接收指向不抛出的函数的指针
// typedef int (*pf)() noexcept; // 错误
#include <iostream>
#include <utility>
#include <vector>
// noexcept 运算符
void may_throw() {};
void no_throw() noexcept {};
auto lmay_throw = [] {};
auto lno_throw = []() noexcept {};
class T {
};
class T1 {
public:
~T1() {}
};
class T2 {
public:
~T2() {}
int v;
};
class T3 {
public:
~T3() {}
std::vector<int> v;
};
class T4 {
public:
std::vector<int> v;
};
int main()
{
T t;
T1 t1;
T2 t2;
T3 t3;
T4 t4;
std::vector<int> vc;
std::cout << std::boolalpha
<< "Is may_throw() noexcept? " << noexcept(may_throw()) << '\n'
<< "Is no_throw() noexcept? " << noexcept(no_throw()) << '\n'
<< "Is lmay_throw() noexcept? " << noexcept(lmay_throw()) << '\n'
<< "Is lno_throw() noexcept? " << noexcept(lno_throw()) << '\n'
<< "Is ~T1() noexcept? " << noexcept(std::declval<T1>().~T1()) << '\n'
<< '\n'
<< '\n'
<< "Is T(rvalue T) noexcept? " << noexcept(T(std::declval<T>())) << '\n'
<< "Is T(lvalue T) noexcept? " << noexcept(T(t)) << '\n'
<< '\n'
<< "Is T1(rvalue T1) noexcept? " << noexcept(T1(std::declval<T1>())) << '\n'
<< "Is T1(lvalue T1) noexcept? " << noexcept(T1(t1)) << '\n'
<< '\n'
<< "Is T2(rvalue T2) noexcept? " << noexcept(T2(std::declval<T2>())) << '\n'
<< "Is T2(lvalue T2) noexcept? " << noexcept(T2(t2)) << '\n'
<< '\n'
<< "Is T3(rvalue T3) noexcept? " << noexcept(T3(std::declval<T3>())) << '\n'
<< "Is T3(lvalue T3) noexcept? " << noexcept(T3(t3)) << '\n'
<< '\n'
<< "Is T4(rvalue T4) noexcept? " << noexcept(T4(std::declval<T4>())) << '\n'
<< "Is T4(lvalue T4) noexcept? " << noexcept(T4(t4)) << '\n'
<< '\n'
<< "Is std::vector<int>(rvalue std::vector<int>) noexcept? " << noexcept(std::vector<int>(std::declval<std::vector<int>>())) << '\n'
<< "Is std::vector<int>(lvalue std::vector<int>) noexcept? " << noexcept(std::vector<int>(vc)) << '\n';
system("pause");
return 0;
}
decltype
decltype
decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。 decltype用法
int getSize();
int main(void)
{
int tempA = 2;
/*1.dclTempA为int*/
decltype(tempA) dclTempA;
/*2.dclTempB为int,对于getSize根本没有定义,但是程序依旧正常,因为decltype只做分析,并不调用getSize,*/
decltype(getSize()) dclTempB;
return 0;
}
double tempA = 3.0;
const double ctempA = 5.0;
const double ctempB = 6.0;
const double *const cptrTempA = &ctempA;
/*1.dclTempA推断为const double(保留顶层const,此处与auto不同)*/
decltype(ctempA) dclTempA = 4.1;
/*2.dclTempA为const double,不能对其赋值,编译不过*/
dclTempA = 5;
/*3.dclTempB推断为const double * const*/
decltype(cptrTempA) dclTempB = &ctempA;
/*4.输出为4(32位计算机)和5*/
cout<<sizeof(dclTempB)<<" "<<*dclTempB<<endl;
/*5.保留顶层const,不能修改指针指向的对象,编译不过*/
dclTempB = &ctempB;
/*6.保留底层const,不能修改指针指向的对象的值,编译不过*/
*dclTempB = 7.0;
```C
* 与引用结合
```C
int tempA = 0, &refTempA = tempA;
/*1.dclTempA为引用,绑定到tempA*/
decltype(refTempA) dclTempA = tempA;
/*2.dclTempB为引用,必须绑定到变量,编译不过*/
decltype(refTempA) dclTempB = 0;
/*3.dclTempC为引用,必须初始化,编译不过*/
decltype(refTempA) dclTempC;
/*4.双层括号表示引用,dclTempD为引用,绑定到tempA*/
decltype((tempA)) dclTempD = tempA;
const int ctempA = 1, &crefTempA = ctempA;
/*5.dclTempE为常量引用,可以绑定到普通变量tempA*/
decltype(crefTempA) dclTempE = tempA;
/*6.dclTempF为常量引用,可以绑定到常量ctempA*/
decltype(crefTempA) dclTempF = ctempA;
/*7.dclTempG为常量引用,绑定到一个临时变量*/
decltype(crefTempA) dclTempG = 0;
/*8.dclTempH为常量引用,必须初始化,编译不过*/
decltype(crefTempA) dclTempH;
/*9.双层括号表示引用,dclTempI为常量引用,可以绑定到普通变量tempA*/
decltype((ctempA)) dclTempI = ctempA;
int tempA = 2;
int *ptrTempA = &tempA;
/*1.常规使用dclTempA为一个int *的指针*/
decltype(ptrTempA) dclTempA;
/*2.需要特别注意,表达式内容为解引用操作,dclTempB为一个引用,引用必须初始化,故编译不过*/
decltype(*ptrTempA) dclTempB;
decltype总结 decltype和auto都可以用来推断类型,但是二者有几处明显的差异: 1.auto忽略顶层const,decltype保留顶层const; 2.对引用操作,auto推断出原有类型,decltype推断出引用; 3.对解引用操作,auto推断出原有类型,decltype推断出引用; 4.auto推断时会实际执行,decltype不会执行,只做分析。 总之在使用中过程中和const、引用和指针结合时需要特别小心。
constexpr
constexpr constexpr意义 将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式(不会改变,在编译过程中就能得到计算结果的表达式)。是一种比const更强的约束,这样可以得到更好的效率和安全性。
constexpr用法
/*1.如果size在编译时能确定,那么返回值就可以是constexpr,编译通过*/
constexpr int getSizeA(int size)
{
return 4*size;
}
/*2.编译通过,有告警:在constexpr中定义变量*/
constexpr int getSizeB(int size)
{
int index = 0;
return 4;
}
/*3.编译通过,有告警:在constexpr中定义变量(这个有点迷糊)*/
constexpr int getSizeC(int size)
{
constexpr int index = 0;
return 4;
}
/*4.编译通过,有告警:使用了if语句(使用switch也会告警)*/
constexpr int getSizeD(int size)
{
if(0)
{}
return 4;
}
/*5.定义变量并且没有初始化,编译不过*/
constexpr int getSizeE(int size)
{
int index;
return 4;
}
/*6.rand()为运行期函数,不能在编译期确定,编译不过*/
constexpr int getSizeF(int size)
{
return 4*rand();
}
/*7.使用了for,编译不过*/
constexpr int getSizeG(int size)
{
for(;0;)
{}
return 4*rand();
}
int tempA;
cin>>tempA;
const int ctempA = 4;
const int ctempB = tempA;
/*1.可以再编译器确定,编译通过*/
constexpr int conexprA = 4;
constexpr int conexprB = conexprA + 1;
constexpr int conexprC = getSizeA(conexprA);
constexpr int conexprD = ctempA;
/*2.不能在编译期决定,编译不过*/
constexpr int conexprE = tempA;
constexpr int conexprF = ctempB;
int g_tempA = 4;
const int g_conTempA = 4;
constexpr int g_conexprTempA = 4;
int main(void)
{
int tempA = 4;
const int conTempA = 4;
constexpr int conexprTempA = 4;
/*1.正常运行,编译通过*/
const int *conptrA = &tempA;
const int *conptrB = &conTempA;
const int *conptrC = &conexprTempA;
/*2.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/
constexpr int *conexprPtrA = &tempA;
constexpr int *conexprPtrB = &conTempA
constexpr int *conexprPtrC = &conexprTempA;
/*3.第一个通过,后面两个不过,因为constexpr int *所限定的是指针是常量,故不能将常量的地址赋给顶层const*/
constexpr int *conexprPtrD = &g_tempA;
constexpr int *conexprPtrE = &g_conTempA
constexpr int *conexprPtrF = &g_conexprTempA;
/*4.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/
constexpr const int *conexprConPtrA = &tempA;
constexpr const int *conexprConPtrB = &conTempA;
constexpr const int *conexprConPtrC = &conexprTempA;
/*5.正常运行,编译通过*/
constexpr const int *conexprConPtrD = &g_tempA;
constexpr const int *conexprConPtrE = &g_conTempA;
constexpr const int *conexprConPtrF = &g_conexprTempA;
return 0;
}
int g_tempA = 4;
const int g_conTempA = 4;
constexpr int g_conexprTempA = 4;
int main(void)
{
int tempA = 4;
const int conTempA = 4;
constexpr int conexprTempA = 4;
/*1.正常运行,编译通过*/
const int &conptrA = tempA;
const int &conptrB = conTempA;
const int &conptrC = conexprTempA;
/*2.有两个问题:一是引用到局部变量,不能再编译器确定;二是conexprPtrB和conexprPtrC应该为constexpr const类型,编译不过*/
constexpr int &conexprPtrA = tempA;
constexpr int &conexprPtrB = conTempA
constexpr int &conexprPtrC = conexprTempA;
/*3.第一个编译通过,后两个不通过,原因是因为conexprPtrE和conexprPtrF应该为constexpr const类型*/
constexpr int &conexprPtrD = g_tempA;
constexpr int &conexprPtrE = g_conTempA;
constexpr int &conexprPtrF = g_conexprTempA;
/*4.正常运行,编译通过*/
constexpr const int &conexprConPtrD = g_tempA;
constexpr const int &conexprConPtrE = g_conTempA;
constexpr const int &conexprConPtrF = g_conexprTempA;
return 0;
}
char32_t
char16_t和char32_t char16_t和char32_t:
产生原因: 随着编程人员日益的熟悉Unicode,类型wchar_t显然已经满足不了需求,在计算机系统上进行的编码字符和字符串编码时,仅仅使用Unicode码点显然是不够的。 比如:如果在进行字符串编码时,如果有特定长度和符号特征的类型将很有帮助,而类型wchar_t的长度和符号特征随实现而已。 因此C++11新增了类型char16_t,char32_t。
char16_t:无符号类型,长16位, char32_t无符号类型,长32位
C++11使用前缀u表示char16_t字符常量和字符串常量如:u‘L’;u“lilili”; C++11使用前缀U表示char32_t字符常量和字符串常量如:U’L’;U"lilili";
类型char16_t与/u00F6形式的通用字符名匹配, 类型char32_t与/U0000222B形式的通用字符名匹配。 前缀u和U分别指出字符字面值的类型为char16_t和char32_t。
注意: 如果你在VS中使用char16_t或者char32_t的话,不要加前缀u或者U只能加前缀L.
char16_t
alignof
alignas
alignof和alignas C++11新引入操作符alignof, 对齐描述符alignas,基本对齐值 alignof(std::max_align_t)
alignas可以接受常量表达式和类型作为参数,可以修饰变量、类的数据成员等,不能修饰位域和用register申明的变量。一般往大对齐。
struct s3
{
char s;
double d;
int i;
};
struct s11
{
alignas(16) char s;
int i;
};
struct s12
{
alignas(16) char s;
int i;
};
// alignof
cout << "-------------------alignof---------------------" << endl;
// 基本对齐值
cout << "alignof(std::max_align_t) " << alignof(std::max_align_t) << endl;
cout << endl;
cout << "-------basic type" << endl;
cout << "alignof(char) " << alignof(char) << endl;
cout << "alignof(int) " << alignof(int) << endl;
cout << "alignof(double) " << alignof(double) << endl;
cout << endl;
cout << "-------struct" << endl;
cout << "alignof(s1) " << alignof(s1) << endl;
cout << "alignof(s2) " << alignof(s2) << endl;
cout << "alignof(s3) " << alignof(s3) << endl;
cout << endl;
cout << endl;
// alignas
cout << "-------------------alignas---------------------" << endl;
cout << "alignof(s1) " << alignof(s1) << endl;
cout << "alignof(s11) " << alignof(s11) << endl;
cout << "alignof(s12) " << alignof(s12) << endl;
cout << "sizeof(s1) " << sizeof(s1) << endl;
cout << "sizeof(s11) " << sizeof(s11) << endl;
cout << "sizeof(s12) " << sizeof(s12) << endl;
//结果如下:
-------------------alignof---------------------
alignof(std::max_align_t) 8
-------basic type
alignof(char) 1
alignof(int) 4
alignof(double) 8
-------struct
alignof(s1) 4
alignof(s2) 8
alignof(s3) 8
-------------------alignas---------------------
alignof(s1) 4
alignof(s11) 16
alignof(s12) 16
sizeof(s1) 4
sizeof(s11) 16
sizeof(s12) 16
含义变化或新增含义关键字
auto
auto C++11标准和C++98/03标准的auto是不同的。C++98/03标准中,auto表示自动储存类型 ;C++11标准中,auto表示由编译器静态判断其应有的类型。
C++98 auto
int a =10 ; //拥有自动生命期
auto int b = 20 ;//拥有自动生命期
static int c = 30 ;//延长了生命期
C++11 auto
auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型,类似的关键字还有decltype。
int a = 10;
auto b = a;//自动推断类型,b为 int类型
auto c = 1.9;//自动推断类型,c为double类型
auto d = 1.2e12L;//自动推断类型,d 是 long double
class
class C++11中对类(class)新增的特性:
- default/delete 控制默认函数
- override /final 强制重写/禁止重写虚函数
- 委托构造函数 Delegating constructors
- 继承的构造函数 Inheriting constructors
- 类内部成员的初始化 Non-static
- 移动构造和移动赋值
default
在C+11中,对于defaulted函数,编译器会为其自动生成默认的函数定义体,从而获得更高的代码执行效率,也可免除程序员手动定义该函数的工作量。
C++的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。当存在用户自定义的特殊成员函数时,编译器将不会隐式的自动生成默认特殊成员函数,而需要程序员手动编写,加大了程序员的工作量。并且手动编写的特殊成员函数的代码执行效率比编译器自动生成的特殊成员函数低。
C++11标准引入了一个新特性:defaulted函数。程序员只需在函数声明后加上”=default;”,就可将该函数声明为defaulted函数,编译器将为显式声明的defaulted函数自动生成函数体。
defaulted函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。
defaulted函数既可以在类体里(inline)定义,也可以在类体外(out-of-line)定义。
#include "default.hpp"
#include <iostream>
class Foo
{
Foo(int x); // Custom constructor
Foo() = default; // The compiler will now provide a default constructor for class Foo as well
};
struct A
{
int x;
A(int x = 1) : x(x) {} // user-defined default constructor
};
struct B : A
{
// B::B() is implicitly-defined, calls A::A()
};
struct C
{
A a;
// C::C() is implicitly-defined, calls A::A()
};
struct D : A
{
D(int y) : A(y) {}
// D::D() is not declared because another constructor exists
};
struct E : A
{
E(int y) : A(y) {}
E() = default; // explicitly defaulted, calls A::A()
};
struct F
{
int& ref; // reference member
const int c; // const member
// F::F() is implicitly defined as deleted
};
int test_default1()
{
A a;
B b;
C c;
// D d; // compile error
E e;
// F f; // compile error
return 0;
}
///
struct widget
{
widget() = default;
inline widget& operator=(const widget&);
};
// Notice that you can default a special member function outside the body of a class as long as it’s inlinable.
inline widget& widget::operator=(const widget&) = default;
delete
delete C++11 中,可在想要 “禁止使用” 的特殊成员函数声明后加 “= delete”,而需要保留的加 “= default” 或者不采取操作
class LeafOfTree{
public:
LeafOfTree() = default;
~LeafOfTree() = default;
LeafOfTree(const LeafOfTree&) = delete; // mark copy ctor or copy assignment operator as deleted functions
LeafOfTree & operator=(const LeafOfTree&) = delete;
};
delete 的扩展 C++11 中,delete 关键字可用于任何函数,不仅仅局限于类成员函数
export
export C++11 中,不使用并保留该关键词
extern
extern 外部模板
extern template<class T>void(T t);
inline
inline C++11中引入了内联命名空间(inline namespace),它的特点就是不需要使用using语句就可以直接在外层命名空间使用该命名空间内部的内容,而且无需使用命名空间前缀。
inline namespace inline_namespacel{
class Inlinel{
public:
int iv;
};
}
namespace inline_namespaceli
class Inline2{
public:
double dv;
};
}
内联命名空间的声明方法就是在原来的声明语法前面增加inline关键字。除此之外上面代码还有以下特点:
两处声明的命名空间同名,它们同属一个命名空间。这是C++命名空间从来就有的特性。
第一次声明命名空间时使用了inline关键字,这叫显式内联;第二次没有使用inline关键字,但是由于第一次已经声明了inline,这里声明的还是内联命名空间。这种情况成为隐式内联。
内联命名空间声明之后,就可以在外层命名空间不适用前缀而直接使用它们了。
namespace inline_test{
inline namespace inline_namespace1{
class Inlinel {
public :
int iv;
};
}
namespace inline_namespace1{
class Inline2{
public :
double dv ;
};
}
void test_inline_namespace(){
Inlinel inl;
inl.iv = 5;
Inline2 in2;in2.dv = 2;
}
}
void test_inline_namespace2(){
inline_test::Inlinel inl;
in1.iv = 5;
inline_test::Inline2 in2;
in2.dv = 2;
}
上述代码中test_inline_namespace处在linline_namespace1的外层,所以可以直接使用Inline1和Inline2。test_inline_namespace2处在更外层,这时也只是需要使用外层命名空间inline_test前缀即可。 看起来inline_namespace就像不存在一样。
mutable
mutable C++11中的mutable是用来修改const函数中的不可修改类成员的缺陷
class Log{
public:
//
void print(const std::string& str) const
{
printf("%s", str_cstr());
//统计输出次数
printNums++;
}
private:
//这里必须声明为mutable
mutable int printNums;
}
sizeof
sizeof 在标准C++,sizeof可以作用在对象以及类别上。但是不能够做以下的事:
struct someType { otherType member; } ;
sizeof(SomeType: :member); //直接由someType型别取得非静态成员的大小,C++03不行。C++11允哥
这会传回OtherType的大小。C++03并不允许这样做,所以会引发编译错误。C++11将会允许这种使用。
#include <iostream>
using namespace std;
struct Empty{};
struct Base{int a;};
struct Derived:Base
{
int b;
};
struct Bit
{
unsigned bit:1;
};
int main()
{
Empty e;
Derived d;
Base& b = d;
Bit bit;
cout << sizeof(e) << endl;
cout << sizeof(Empty) << endl;
cout << sizeof(&e) << endl;
cout << sizeof(Derived) << endl;
cout << sizeof(d) << endl;
cout << sizeof(void) << endl;//BAD
return 0;
}
struct
struct C++11 struct可以给每个成员变量赋予默认的初始值
struct Student{
char* name = nullptr;
unsigned int age = 15;
int number = 21509111;
};
所有声明的新结构体对象就是默认上面的值。
using
using
- using 在 C++11之前主要用于名字空间、类型、函数与对象的引入,实际上是去除作用域的限制。
//引入名字空间
using namespace std;
//引入类型
using std::iostream;
//引入函数
using std::to_string;
//引入对象
using std::cout;
通过using引入函数可以解除函数隐藏 “隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下: 1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆) 2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) 使用了using关键字,就可以避免1的情况,是的父类同名函数在子类中得以重载,不被隐藏
class Base{
public:
void func() { cout << "in Base::func()" << endl; }
void func(int n) { cout << "in Base::func(int)" << endl;}
};
class Sub : public Base {
public:
using Base::func; //引入父类所有同名函数func,解除函数隐藏
void func() { cout<<"in Sub::func()"<<endl;}
};
int main() {
Sub s;
s.func();
s.func(1); // Success!
}
使用 using 代替 typedef,给类型命名
using uint8=unsigned char; //等价于typedef unsigned char uint8;
using FunctionPtr = void (*)(); //等价于typedef void (FunctionPtr)();
template using MapString = std::map<T, char>; //定义模板别名,注意typedef无法定义模板别名,因为typedef只能作用于具体类型而非模板
STL容器
STL容器
std::array
std::forward_list
std::begin
std::end
std::move
容器初始化
emplace
无序容器
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
元组std::tuple
std::make_tuple
std::get
std::tie
新语法
预处理
语法:__pragma(字符串字面量)
_Pragma运算符
__pragma("once")
C++宏(cplusplus macro)
_cplusplus宏
//_cplusplus宏经常出现在C与C++混合编写的代码中, 一般放在头文件中,比如
#ifdef __cplusplus
extern "C" {
#endif
//some code ...
#ifdef __cplusplus
}
#endif
基于范围的for语句
for循环 for(x:range)
#include <iostream>
#include <vector>
int main()
{
//遍历字符串
std::string str = “hello, world”;
for(auto ch : str)
{
std::cout << ch << std::endl;
}
//遍历str,输出每个字符,同时用上auto,简直是如虎添翼。(auto也是c++11的新特性)
//遍历数组
int arr[] = {1, 2, 3, 4};
for(auto i : arr)
{
std::cout<< i << std::endl;
}
//不用知道数组容器的大小,即可方便的遍历数组。
//遍历stl 容器
std::vector<std::string> str_vec = {“i”, “like”, "google”};
for(auto& it : str_vec)
{
it = “c++”;
}
//在这段程序中,可以返回引用值,通过引用可以修改容器内容。
//遍历stl map
std::map<int, std::string> hash_map = {{1, “c++”}, {2, “java”}, {3, “python”}};
for(auto it : hash_map)
{
std::cout << it.first << “\t” << it.second << std::endl;
}
//遍历map返回的是pair变量,不是迭代器。
return 0;
}
对齐支持(alignment support)
alignof
#include <iostream>
struct Foo {
int i;
float f;
char c;
};
// Note: `alignas(alignof(long double))` below can be simplified to simply
// `alignas(long double)` if desired.
struct alignas(alignof(long double)) Foo2 {
// put your definition here
};
struct Empty {};
struct alignas(64) Empty64 {};
int main()
{
std::cout << "Alignment of" "\n"
"- char : " << alignof(char) << "\n"
"- pointer : " << alignof(int*) << "\n"
"- class Foo : " << alignof(Foo) << "\n"
"- class Foo2 : " << alignof(Foo2) << "\n"
"- empty class : " << alignof(Empty) << "\n"
"- alignas(64) Empty: " << alignof(Empty64) << "\n";
}
alignas
#include <iostream>
int main()
{
alignas(double) unsigned char c[1024]; //字符数组,但是却以double数据的形式对齐数据
alginas(16) char d[100]; //以16字节对齐
return 0;
}
std::alignment_of
std::aligned_storage
std::max_align_t
std::align
显式转换操作符(explicit conversion operators)
explicit关键字
#include <iostream>
using namespace std;
class ConvertTo
{
};
class Convertable
{
public:
explicit operator ConvertTo () const
{
cout << "callCount : " << ++(m_nCallCnt) << endl;
return ConvertTo();
}
static int m_nCallCnt;
};
int Convertable::m_nCallCnt = 0;
void Func(ConvertTo ct) { }
int main()
{
Convertable c;
ConvertTo ct(c); // 直接初始化,通过
// ConvertTo ct2 = c; // 拷贝构造初始化,编译失败
ConvertTo ct3 = static_cast<ConvertTo>(c); // 强制转化,通过
// Func(c); // 拷贝构造初始化,编译失败
system("pause");
}
静态断言(static assert)
语法:static_assert(常量表达式,“提示字符串”)
static assert
#include <cassert>
#include <cstring>
using namespace std;
template <typename T, typename U> int bit_copy(T& a, U& b)
{
assert(sizeof(b) == sizeof(a));
//static_assert(sizeof(b) == sizeof(a), "template parameter size no equal!");
memcpy(&a, &b, sizeof(b));
};
int main()
{
int varA = 0x2468;
double varB;
bit_copy(varA, varB);
getchar();
return 0;
}
数字限制(numeric limits)
#include "numeric_limits.hpp"
#include <limits>
#include <iostream>
int main()
{
std::cout << std::boolalpha;
std::cout << "Minimum value for int: " << std::numeric_limits<int>::min() << std::endl;
std::cout << "Maximum value for int: " << std::numeric_limits<int>::max() << std::endl;
std::cout << "int is signed: " << std::numeric_limits<int>::is_signed << std::endl;
std::cout << "Non-sign bits in int: " << std::numeric_limits<int>::digits << std::endl;
std::cout << "int has infinity: " << std::numeric_limits<int>::has_infinity << std::endl;
std::cout << "Minimum value for float: " << std::numeric_limits<float>::min() << std::endl; // min returns the smallest positive value the type can encode, not the lowest
std::cout << "Lowest value for float: " << std::numeric_limits<float>::lowest() << std::endl; // the lowest value
std::cout << "Maximum value for float: " << std::numeric_limits<float>::max() << std::endl;
std::cout << "float is signed: " << std::numeric_limits<float>::is_signed << std::endl;
std::cout << "Non-sign bits in float: " << std::numeric_limits<float>::digits << std::endl;
std::cout << "float has infinity: " << std::numeric_limits<float>::has_infinity << std::endl;
std::cout << "Minimum value for unsigned short: " << std::numeric_limits<unsigned short>::min() << std::endl;
std::cout << "Maximum value for unsigned short: " << std::numeric_limits<unsigned short>::max() << std::endl;
std::cout << "is_specialized(float): " << std::numeric_limits<float>::is_specialized << std::endl;
std::cout << "is_integer(float): " << std::numeric_limits<float>::is_integer << std::endl;
std::cout << "is_exact(float): " << std::numeric_limits<float>::is_exact << std::endl;
std::cout << "is_bounded(float): " << std::numeric_limits<float>::is_bounded << std::endl;
std::cout << "is_modulo(float): " << std::numeric_limits<float>::is_modulo << std::endl;
std::cout << "is_iec559(float): " << std::numeric_limits<float>::is_iec559 << std::endl;
std::cout << "digits10(float): " << std::numeric_limits<float>::digits10 << std::endl;
std::cout << "radix(float): " << std::numeric_limits<float>::radix << std::endl;
std::cout << "min_exponent(float): " << std::numeric_limits<float>::min_exponent << std::endl;
std::cout << "max_exponent(float): " << std::numeric_limits<float>::max_exponent << std::endl;
std::cout << "min_exponent10(float): " << std::numeric_limits<float>::min_exponent10 << std::endl;
std::cout << "max_exponent10(float): " << std::numeric_limits<float>::max_exponent10 << std::endl;
std::cout << "epsilon(float): " << std::numeric_limits<float>::epsilon() << std::endl;
std::cout << "round_style(float): " << std::numeric_limits<float>::round_style << std::endl;
std::cout << "The smallest nonzero denormalized value for float: "
<< std::numeric_limits<float>::denorm_min()<< std::endl;
std::cout << "The difference between 1 and the smallest value greater than 1 for float: "
<< std::numeric_limits<float>::epsilon()<< std::endl;
std::cout << "Whether float objects allow denormalized values: "
<< std::numeric_limits<float>::has_denorm << std::endl;
std::cout << "Whether float objects can detect denormalized loss: "
<< std::numeric_limits<float>::has_denorm_loss << std::endl;
std::cout << "Whether float objects have quiet_NaN: "
<< std::numeric_limits<float>::has_quiet_NaN << std::endl;
std::cout << "Whether float objects have a signaling_NaN: "
<< std::numeric_limits<float>::has_signaling_NaN << std::endl;
std::cout << "The base for type float is: "
<< std::numeric_limits<float>::radix << std::endl;
std::cout << "The maximum rounding error for type float is: "
<< std::numeric_limits<float>::round_error() << std::endl;
std::cout << "The rounding style for a double type is: "
<< std::numeric_limits<double>::round_style << std::endl;
std::cout << "The signaling NaN for type float is: "
<< std::numeric_limits<float>::signaling_NaN() << std::endl;
std::cout << "Whether float types can detect tinyness before rounding: "
<< std::numeric_limits<float>::tinyness_before << std::endl;
std::cout << "Whether float types have implemented trapping: "
<< std::numeric_limits<float>::traps << std::endl;
return 0;
}
原始字符串(raw string)
#include <iostream>
using std::cout;
using std::endl;
using namespace std;
//原始字符串(raw string)就是字符表示的就是自己,引号和斜杠均无需\进行转义,这在需要输出很多引号和斜杠代码中很方便。
//原始字符串是C++11新增的一个功能,程序中使用R"(a string)"来标识原始字符串:
//原始字符串同时包含其它特点:
//1. 字符串中的换行符将在屏幕上如实显示。
//2. 在表示字符串开头的"和(之间可以添加其它字符,不过必须在表示字符串结尾的)和"之间添加同样的字符。
string path = "C:\Program Files (x86)\alipay\aliedit\5.1.0.3754";
string path2 = "C:\\Program Files (x86)\\alipay\\aliedit\\5.1.0.3754";
//更简洁的表示string path3 = R"(C:\Program Files (x86)\alipay\aliedit\5.1.0.3754)";
string path4 = R"(C:\Program "Files" (x86)\\alipay\aliedit\5.1.0.3754)";
int main(int argc, char *argv[])
{
cout<<path<<endl;
cout<<path2<<endl;
cout<<path3<<endl;
cout<<path4<<endl;
cout << "I print \'\\\', \"\\n\" as I like." << endl;
cout << R"(I print '\', "\n" as I like.)" << endl;
cout << R"(I print '\', "\n" as I like.)" << endl;
cout << R"haha(I print '\', "\n" and )" as I like.)haha" << endl;
return 0;
}
追踪返回类型语法(trailing return type syntax)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <vector>
#include <map>
int func(int, int);
auto func2(int, int)->int; // 指定返回类型
template<typename T1, typename T2>
auto sum(const T1 &t1, const T2 &t2)->decltype(t1+t2) // 指定返回类型
{
return t1 + t2;
}
template<typename T1, typename T2>
auto mul(const T1 &t1, const T2 &t2)->decltype(t1*t2) // 指定返回类型
{
return t1 * t2;
}
void mytest()
{
auto a = 3;
auto b = 4L;
auto pi = 3.14f;
auto c = mul(sum(a, b), pi);
std::cout << c << std::endl; // 21.98
return;
}
int main()
{
mytest();
system("pause");
return 0;
}
扩展的friend语法(extended friend syntax)
class Poly;
typedef Poly P;
class LiLei
{
friend class Poly; // C++98通过, C++11通过
};
class Jim
{
friend Poly; // C++98失败, C++11通过
};
class HanMei
{
friend P; // C++98失败, C++11通过
};
扩展的整型(extended integer types)
//对于有符号的,下面的类型是等价的:
//long long、signed long long、long long int、signed long long int; 而unsigned long long 和 unsigned long long int 也是等价的。
//与 long long 整型相关的一共有3个:
//LONG_MIN、LONG_MAX 和ULONG_MAX, 它们分别代表了平台上最小的long long 值、最大的long long 值,以及最大的unsigned long long 值。
int main() {
long long int lli=-900000000000000LL; // 有符号的long long 变量lli
unsigned long long int ulli=-900000000000ULL; // 无符号的 unsigned long long 变量ulli
return 0;
}
非受限联合体(unrestricted union)
//非受限联合体:C++98中并不是所有数据类型都能够成为union的数据成员,不允许联合体拥有非POD(Plain Old Data)、静态或引用类型的成员。
//C++11中取消了联合体对于数据成员的限制,任何非引用类型都可以成为联合体的数据成员,成为非受限联合体。
struct Student
{
Student(bool g, int a): gender(g), age(a){}
bool gender;
int age;
};
union T
{
Student s; //C++98下编译失败,不是一个POD类型
int id;
char name[10];
};
int main()
{
return 0;
}
//编译选项:g++ -std=c++98 union.cpp
内联名字空间(lnline namespace)
namespace Parent
{
inline namespace Child1
{
struct child1_data{int a;} ;
}
inline namespace Child2
{
struct child2_data{int b;} ;
}
namespace child3
{
child1_data data1;
child2_data data2;
}
}
用户定义的字面量(user-defined literals)
#include <cstddef>
#include <algorithm>
#include <iostream>
#include <numbers>
#include <string>
// used as conversion from degrees (input param) to radians (returned output)
constexpr long double operator"" _deg_to_rad ( long double deg )
{
long double radians = deg * std::numbers::pi_v<long double> / 180;
return radians;
}
// used with custom type
struct mytype
{
unsigned long long m;
};
constexpr mytype operator"" _mytype ( unsigned long long n )
{
return mytype{n};
}
// used for side-effects
void operator"" _print ( const char* str )
{
std::cout << str << '\n';
}
#if __cpp_nontype_template_args < 201911
std::string operator"" _x2 ( const char* str, std::size_t )
{
return std::string{str} + str;
}
#else // C++20 string literal operator template
template<std::size_t N>
struct DoubleString
{
char p[N*2-1]{};
constexpr DoubleString ( char const(&pp)[N] )
{
std::ranges::copy(pp, p);
std::ranges::copy(pp, p + N - 1);
};
};
template<DoubleString A>
constexpr auto operator"" _x2()
{
return A.p;
}
#endif // C++20
int main()
{
double x_rad = 90.0_deg_to_rad;
std::cout << std::fixed << x_rad << '\n';
mytype y = 123_mytype;
std::cout << y.m << '\n';
0x123ABC_print;
std::cout << "abc"_x2 << '\n';
}
强类型枚举(scoped and strongly typed enums)
//强类型枚举以及c++11对原有枚举类型的扩展
//声明强类型枚举,只需要在enum加上关键字class。
//enum class Type { General, Light, Medium, Heavy };
//优点:
//(1)强作用域,强类型枚举成员的名称不会被输出到其父作用域空间
//(2)转换限制,强类型枚举成员的值不可以与整形隐式地相互转换
//(3)可以指定底层类型。强类型枚举默认的底层类型为int,但也可以显式地指定底层类型。
//比如:
//enum class Type: char { General, Light, Medium, Heavy };
#include <iostream>
using namespace std;
enum class Type { General, Light, Medium, Heavy };
enum class Category { General = 1, Pistol, MachineGun, Cannon };
int main() {
Type t = Type::Light;
t = General; //编译失败,必须使用强类型名称
if (t == Category::General) //编译失败,必须使用Type中的General
cout << "General Weapon" << endl;
if (t > Type::General) //通过编译
cout << "Not General Weapon" << endl;
if (t > 0) //编译失败,无法转换为int类型
cout << "Not General Weapon" << endl;
if ((int)t > 0) //通过编译
cout << "Not General Weapon" << endl;
return 0;
}
随机装置(random device)
#include <vector>
#include <iostream>
#include <random>
#include <ctime>
std::vector<unsigned> randomGenerate(const unsigned low, const unsigned high)
{
static std::default_random_engine e(time(0));
static std::uniform_int_distribution<unsigned> u(low, high);
std::vector<unsigned> vec;
for (int i = 0; i < 10; i++)
vec.push_back(u(e));
return vec;
}
int main()
{
for (int i = 0; i < 10; i++) {
std::vector<unsigned> vec = randomGenerate(0, 30);
for (auto &i : vec)
std::cout << i << " ";
std::cout << std::endl;
}
return 0;
}
std::ref和std::cref
//std::ref 用于包装按引用传递的值。
//std::cref 用于包装按const引用传递的值。
#include<iostream>
#include<thread>
#include<string>
using namespace std;
void foo( int &a)
{
cout<<"thread :"<< a++ <<endl;
}
int main()
{
int num = 0;
thread t1(foo, std::ref(num));
thread t2(foo, std::ref(num));
t1.join();
t2.join();
return 0;
}
常量表达式(constexpr)
int g_tempA = 4;
const int g_conTempA = 4;
constexpr int g_conexprTempA = 4;
int main(void)
{
int tempA = 4;
const int conTempA = 4;
constexpr int conexprTempA = 4;
/*1.正常运行,编译通过*/
const int *conptrA = &tempA;
const int *conptrB = &conTempA;
const int *conptrC = &conexprTempA;
/*2.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/
//constexpr int *conexprPtrA = &tempA;
//constexpr int *conexprPtrB = &conTempA
//constexpr int *conexprPtrC = &conexprTempA;
/*3.第一个通过,后面两个不过,因为constexpr int *所限定的是指针是常量,故不能将常量的地址赋给顶层const*/
constexpr int *conexprPtrD = &g_tempA;
constexpr int *conexprPtrE = &g_conTempA
constexpr int *conexprPtrF = &g_conexprTempA;
/*4.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/
//constexpr const int *conexprConPtrA = &tempA;
//constexpr const int *conexprConPtrB = &conTempA;
//constexpr const int *conexprConPtrC = &conexprTempA;
/*5.正常运行,编译通过*/
constexpr const int *conexprConPtrD = &g_tempA;
constexpr const int *conexprConPtrE = &g_conTempA;
constexpr const int *conexprConPtrF = &g_conexprTempA;
return 0;
}
lamda表达式
#include <iostream>
int main() {
int m = [](int x) { return [](int y) { return y * 2; }(x)+6; }(5);
std::cout << "m:" << m << std::endl; //输出m:16
std::cout << "n:" << [](int x, int y) { return x + y; }(5, 4) << std::endl; //输出n:9
auto gFunc = [](int x) -> function<int(int)> { return [=](int y) { return x + y; }; };
auto lFunc = gFunc(4);
std::cout << lFunc(5) << std::endl;
auto hFunc = [](const function<int(int)>& f, int z) { return f(z) + 1; };
auto a = hFunc(gFunc(7), 8);
int a = 111, b = 222;
auto func = [=, &b]()mutable { a = 22; b = 333; std::cout << "a:" << a << " b:" << b << std::endl; };
func();
std::cout << "a:" << a << " b:" << b << std::endl;
a = 333;
auto func2 = [=, &a] { a = 444; std::cout << "a:" << a << " b:" << b << std::endl; };
func2();
auto func3 = [](int x) ->function<int(int)> { return [=](int y) { return x + y; }; };
std::function<void(int x)> f_display_42 = [](int x) { print_num(x); };
f_display_42(44);
}
指针空值(nullptr)
#include <iostream>
using std::cout;
using std::endl;
namespace myname {
struct nullptr_t
{
void * _;
struct __nat {int __for_bool_;};
nullptr_t(int __nat::*) {} // 构造函数,参数可以是0(空指针),或者&__nat::__for_bool_
operator int __nat::*() const {return 0;} // 不理解为什么转换为bool型时会调用此转换函数
// operator bool() const {return 0; /* or return false; */} // 为什么不这样实现?
template <class _Tp>
operator _Tp* () const {return 0;} // 转换为非成员变量指针、非成员函数指针的普通指针
template <class _Tp, class _Up>
operator _Tp _Up::* () const {return 0;} // 转换为成员变量指针或成员函数指针
friend bool operator==(nullptr_t, nullptr_t) {return true;}
friend bool operator!=(nullptr_t, nullptr_t) {return false;}
friend bool operator<(nullptr_t, nullptr_t) {return false;}
friend bool operator<=(nullptr_t, nullptr_t) {return true;}
friend bool operator>(nullptr_t, nullptr_t) {return false;}
friend bool operator>=(nullptr_t, nullptr_t) {return true;}
};
struct Test {};
}
using namespace myname;
inline nullptr_t __get_nullptr_t() {return nullptr_t(0);}
#define nullptr __get_nullptr_t()
int main(int argc, char *argv[])
{
char *pch = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template <class _Tp> nullptr_t::operator _Tp* () const
int *pint = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template <class _Tp> nullptr_t::operator _Tp* () const
bool b = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => nullptr_t::operator int __nat::*() const
//int n = nullptr; // error: cannot convert 'myname::nullptr_t' to 'int' in initialization
int Test::*p = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template <class _Tp, class _Up> operator _Tp _Up::* () const
void (Test::*pfn)(int, char) = nullptr; // ok, __get_nullptr_t() => nullptr_t::nullptr_t(0) => template <class _Tp, class _Up> operator _Tp _Up::* () const
return 0;
}
防止类型收窄(Preventing narrowing)
#include <iostream>
int main(int argc, char* argv[])
{
int a = 1.1;
int b{1};
//int b{1,1};
float f1 = 1e40;
float f2{10.0};
//float f2{1e40};
const int x = 1024, y = 1;
int dd{x};
char c = x;
// char d{x};
char e = y;
char f{y};
return 0;
}
//g++ -std=c++11 002_grammar_preventing_narrowing.cpp -o preventing_narrowing 编译
初始化列表(initializer lists)
初始化列表——Initializer List
#include <iostream>
#include <vector>
#include <list>
#include <map>
using namespace std;
//初始化列表
int main(int argc, char *argv[])
{
vector<int> iv = {1,2,3,4,5};
list<int> li = {1,2,3,4,5};
map<int,string> mis = {{1,"c"},{2,"c++"},
{3,"java"},{4,"scala"},
{5,"python"}};
mis.insert({6,"ruby"});
// map<int,string>::iterator itr = mis.begin();
// for(; itr != mis.end(); ++itr)
// {
// cout<<itr->first<< itr->second<<endl;
// }
for(auto &is: mis)
{
cout<<is.first<<is.second<<endl;
}
return 0;
}
initializer_list(作入参)
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
class MyArray
{
private:
vector<T> m_Array;
public:
MyArray() { }
MyArray(const initializer_list<T>& il)
{
for (auto x : il)
m_Array.push_back(x);
}
};
int main()
{
MyArray<int> foo = { 3, 4, 6, 9 };
return 0;
}
统一的初始化语法和语义(Uniform initialization syntax and semantics)
// C++中的初始化风格,大体有如下形式:
// int a = 2; //"赋值风格"的初始化
// int aa [] = { 2, 3 }; //用初始化列表进行的赋值风格的初始化
// complex z(1, 2); //"函数风格"的初始化
// C++ 11 中,允许通过以花括号的形式来调用构造函数
// int a = { 2 };
// int aa [] = { 2, 3 };
// complex z = { 1, 2 };
#include <iostream>
using namespace std;
class complex
{
public:
complex(int x, int y)
:_x(x),_y(y){}
private:
int _x;
int _y;
};
complex func(const complex & com)
{
return {1,2};
}
int main(int argc, char *argv[])
{
int a = 10;
int aa[] = {1,2,3};
complex com(1,2);
int a_ = {1};
int aa_[] = {1,2,3};
complex com_ = {1,2};
func({1,2});
return 0;
}
POD(plain old data)
//一个对象既是普通类型(Trivial Type)又是标准布局类型(Standard-layout Type)那么这个对象就是POD类型。
//为什么我们需要 POD 类型满足这些条件呢,POD 类型在源码层级的操作上兼容于 ANSI C。POD 对象与 C 语言中的
//对象具有一些共同的特性,包括初始化、复制、内存布局与寻址:
//(1)可以使用字节赋值,比如用 memset、memcpy 对 POD 类型进行赋值操作;
//(2)对 C 内存布局兼容,POD 类型的数据可以使用 C 函数进行操作且总是安全的;
//(3)保证了静态初始化的安全有效,静态初始化可以提高性能,如将 POD 类型对象放入 BSS 段默认初始化为 0。
//POD类型的二进制拷贝案例
#include <iostream>
using namespace std;
class A {
public:
int x;
double y;
};
int main() {
if (std::is_pod<A>::value) {
std::cout << "before" << std::endl;
A a;
a.x = 8;
a.y = 10.5;
std::cout << a.x << std::endl;
std::cout << a.y << std::endl;
size_t size = sizeof(a);
char *p = new char[size];
memcpy(p, &a, size);
A *pA = (A*)p;
std::cout << "after" << std::endl;
std::cout << pA->x << std::endl;
std::cout << pA->y << std::endl;
delete p;
}
return 0;
}
long long整型
/*
g++ -D__STDC_FORMAT_MACROS -o test_int64 -g -O0 002_grammar_long_long.cpp.cpp
*/
#include <stdio.h>
#include <inttypes.h>
int main(int argc, char** argv){
long long int lli=-900000000000000LL; // 有符号的long long 变量lli
unsigned long long int ulli=-900000000000ULL; // 无符号的 unsigned long long 变量ulli。
int64_t value = 0xFFFFFFFFFFFF;
printf("int64_t=%"PRId64", sizeof(int64_t)=%d\n", value, sizeof(int64_t));
return 0;
}
移动语义(move semantics)
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
class MyString
{
public:
static size_t CCtor; //统计调用拷贝构造函数的次数
static size_t MCtor; //统计调用移动构造函数的次数
static size_t CAsgn; //统计调用拷贝赋值函数的次数
static size_t MAsgn; //统计调用移动赋值函数的次数
public:
// 构造函数
MyString(const char* cstr=0){
if (cstr) {
m_data = new char[strlen(cstr)+1];
strcpy(m_data, cstr);
}
else {
m_data = new char[1];
*m_data = '\0';
}
}
// 拷贝构造函数
MyString(const MyString& str) {
CCtor ++;
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
}
// 移动构造函数
MyString(MyString&& str) noexcept
:m_data(str.m_data) {
MCtor ++;
str.m_data = nullptr; //不再指向之前的资源了
}
// 拷贝赋值函数 =号重载
MyString& operator=(const MyString& str){
CAsgn ++;
if (this == &str) // 避免自我赋值!!
return *this;
delete[] m_data;
m_data = new char[ strlen(str.m_data) + 1 ];
strcpy(m_data, str.m_data);
return *this;
}
// 移动赋值函数 =号重载
MyString& operator=(MyString&& str) noexcept{
MAsgn ++;
if (this == &str) // 避免自我赋值!!
return *this;
delete[] m_data;
m_data = str.m_data;
str.m_data = nullptr; //不再指向之前的资源了
return *this;
}
~MyString() {
delete[] m_data;
}
char* get_c_str() const { return m_data; }
private:
char* m_data;
};
size_t MyString::CCtor = 0;
size_t MyString::MCtor = 0;
size_t MyString::CAsgn = 0;
size_t MyString::MAsgn = 0;
int main()
{
vector<MyString> vecStr;
vecStr.reserve(1000); //先分配好1000个空间
for(int i=0;i<1000;i++){
vecStr.push_back(MyString("hello"));
}
cout << "CCtor = " << MyString::CCtor << endl;
cout << "MCtor = " << MyString::MCtor << endl;
cout << "CAsgn = " << MyString::CAsgn << endl;
cout << "MAsgn = " << MyString::MAsgn << endl;
}
右值引用(rvalue reference)
#include "stdio.h"
#include <string>
#include <iostream>
using namespace std;
class MyString
{
public:
MyString() :m_pData(NULL), m_nLen(0)
{
cout << "MyString()" << endl;
}
MyString(const char *pStr) // 允许隐式转换
{
cout << "MyString(const char *pStr)" << endl;
m_nLen = strlen(pStr);
CopyData(pStr);
}
MyString(const MyString& other)
{
cout << "MyString(const MyString& other)" << endl;
if (!other.m_pData)
{
m_nLen = other.m_nLen;
DeleteData();
CopyData(other.m_pData);
}
}
MyString& operator=(const MyString& other)
{
cout << "MyString& operator=(const MyString& other)" << endl;
if (this != &other)
{
m_nLen = other.m_nLen;
DeleteData();
CopyData(other.m_pData);
}
return *this;
}
MyString(MyString&& other)
{
cout << "MyString(MyString&& other)" << endl;
m_nLen = other.m_nLen;
m_pData = other.m_pData;
other.m_pData = NULL;
}
MyString& operator=(MyString&& other)
{
cout << "MyString& operator=(const MyString&& other)" << endl;
if (this != &other)
{
m_nLen = other.m_nLen;
m_pData = other.m_pData;
other.m_pData = NULL;
}
return *this;
}
~MyString()
{
DeleteData();
}
private:
void CopyData(const char *pData)
{
if (pData)
{
m_pData = new char[m_nLen + 1];
memcpy(m_pData, pData, m_nLen);
m_pData[m_nLen] = '\0';
}
}
void DeleteData()
{
if (m_pData != NULL)
{
delete[] m_pData;
m_pData = NULL;
}
}
private:
char *m_pData;
size_t m_nLen;
};
MyString Fun()
{
MyString str = "hello world";
return str;
}
void main()
{
MyString str1 = "hello";
MyString str2(str1);
MyString str3 = Fun();
}
c99特性(c99)
//C++对以下C99特性的支持纳入了新标准之中:
//1、C99中的预定义宏
//2、__func__预定义标识符
//3、_Pragma操作符
//4、不定参数宏定义以及__VA_ARGS__
//5、宽窄字符串连接
#include <iostream>
using namespace std;
int main()
{
cout << "Standerd Clib" << __STDC_HOSTED__ << endl;
cout << "Standerd C" << __STDC__ << endl;
//cout << "C Standerd version " << __STDC_VERSION__ << endl;
//cout << "ISO/IEC" << __STDC_ISO_10646__ << endl;
return 0;
}
一般化的SFINAE规则(generalized SFINAE rules)
#include <type_traits>
#include <iostream>
#include <string>
namespace detail { struct inplace_t{}; }
void* operator new(std::size_t, void* p, detail::inplace_t) {
return p;
}
// enabled via the return type
template<class T,class... Args>
typename std::enable_if<std::is_trivially_constructible<T,Args&&...>::value>::type
construct(T* t,Args&&... args)
{
std::cout << "constructing trivially constructible T\n";
}
// enabled via a parameter
template<class T>
void destroy(T* t,
typename std::enable_if<std::is_trivially_destructible<T>::value>::type* = 0)
{
std::cout << "destroying trivially destructible T\n";
}
// enabled via a template parameter
template<class T,
typename std::enable_if<
!std::is_trivially_destructible<T>{} &&
(std::is_class<T>{} || std::is_union<T>{}),
int>::type = 0>
void destroy(T* t)
{
std::cout << "destroying non-trivially destructible T\n";
t->~T();
}
int main()
{
std::aligned_union_t<0,int,std::string> u;
construct(reinterpret_cast<int*>(&u));
destroy(reinterpret_cast<int*>(&u));
construct(reinterpret_cast<std::string*>(&u),"Hello");
destroy(reinterpret_cast<std::string*>(&u));
}
|