C/C++教程

C++11 麻辣烫

本文主要是介绍C++11 麻辣烫,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

C++11

空格

vector<list<list> >; // C++11之前需要一个空格
vector<list<list>>;  // OK 在C++11之后 

nullptr和std::nullptr_t

// 空指针可以设置成 nullptr
void f(int);
void f(void*);
f(0);            // call f(int)
f(NULL);         // call f(int) if NULL == 0
f(nullptr);      // call f(void*) 
typedef decltype(nullptr) nullptr_t; // 所以nullptr_t就是nullptr的类型

decltype

// 用于定义变量而不必关注类型
int i;
decltype(i) a;

类型自动推导

auto a = {1,2}; // a是一个int类型,用于模板和lambda函数的返回值等复杂类型的情况

变量初始化的 标准化,全部都可以用大括号初始化变量,幕后是array结构在支撑

int val[] {1, 2, 3};
vector<int> vec{1, 2, 3};
vector<string> cites{"a", "a", "a", "a", "a", "a"};

initializer lists (值得重看)

int i;    // i有未定义的值
int j{};  // j 初始化成0
int* p;   // p有未定义的值
int* q{}; // q被初始化成nullptr

int x1(5.1);  // OK,但是x1会是5
int x2 = 5.3; // OK,但是x2会是5
int x3{5.3};  // ERROR,强制转换失败
int x4 = {5.3}; // ERROR,强制转换失败

class P {
  P(int a, int b) 
  {
    cout << a << b << endl;
  }
  P(initializer_list<int> il) 
  {
    for (auto &i :il) 
      cout << i << " ";
    cout << endl;
  }
}
P p(3, 5);	// 调用P(int a, int b)
P p{3, 5};  // 调用P(initializer_list il),但是没有这个函数的时候就调用另一个构造函数了
P p{1, 3, 6};  // 调用P(initializer_list il)

int a = min{1, 3, 5};    // 原来的min版本只能求2个元素的最小值,现在可以求不限个数了

explict

// 用途很少,主要用在构造函数, 防止隐式转换,下面是例子
Class Complex
{
  int real, img;
  explict /* 告诉编译器要执行严格的类型转换 */
  Complex(int re, int im = 0) : real(re), img(im)
  {}
  Complex operator+(const, Complex& x) 
  { return Complex(real + x.real, img + x.img);}
};

新的for循环

for (decl : coll) 
{ statement; }

=default 和 =delete

以下面的例子为例:

Class Zoo
{
public:
  Zoo(int il1, int il1) : il1(i1), il2(i2) {}
  Zoo(const Zoo&) = delete;
  Zoo(Zoo&&) = default;
  Zoo& operator = Zoo(const Zoo&) = default;
  Zoo& operator = Zoo(const Zoo&&) = delete;
  vitual ~Zoo();
private:
  int i1, i2;
};

原来的情况是如果你写了上面的一个类,但是没有定义任何的ctor(包括无参构造函数、析构函数、拷贝构造函数、参数是引用的move构造函数、赋值构造函数和参数是引用的move构造函数),C++编译器将会帮你写出来;但是只要你主动声明了这些函数,C++编译器就不会做这个事情。

那么现在如果对其中的某个函数加上了delete就表示不需要这个函数。 而如果加上了delete就表示需要这个函数。

btw:如果default用在其他函数的话,没有意义,只会编译报错;如果delete则

Alias(别名) Template (template typedef)

原本可以用typedef就有相似的功能,可是有无法传递参数的短板。

现在用using创建别名,就不会有这样的短板。

当然,不仅与此,别名可以处理模板的模板参数,二阶模板参数

值得回味。

类型的化名

还是using。

  1. 函数指针:typedef void (*)(int, int);变成using func = void (*)(int, int)
  2. 模板的化名:template<class CharT> using mystring = std::basic_string<CharT, std::char_trais<CharT>>; mystring<char> str; // mystring就是化名

noexcept

void foo() noexcept; // void foo() noexcept(true);

override

用在虚函数身上,强制让函数签名完全相同。

final

用在2个地方:

  1. 告诉编译器这个类不能被继承。
  2. 告诉编译器这个函数不能override。

Lambdas

定义inline函数(仿函数、函数对象)。

  1. 方括号开始
  2. 括号表示变量
  3. 大括号是函数体

简单的例子:

[]{std::out << "hello world" << std::endl;} // why not call it directly
[]{std::out << "hello lambda" << std::endl;}() // print lambda
auto I = []{std::out << "hello lambda 2" << std::endl;} // 得到函数对象
I(); // print lambda 2
// 完整形式
[...](...)mutable throwSpec -> retType {...}
// mutable 关系到方括号内的数据是否可以被改写
// 表示可以产生异常
// 向右的箭头后面代表返回类型
// 小括号里是参数
// 中括号代表这是lambda函数开始
// 中括号里面的内容
// 如果 mutable、throwSpec和ret都不存在,那么括号可以不写。
// 方括号[]内如果是=等号,就表示以值(而且是常量不能改变)的方式捕获外部变量;如果是&就表示以引用的方式捕获外部变量。
// lambda的函数体必须不能太复杂,否则报错之后晦涩的错误信息将会极度地降低效率。

Variadic Template

例1:

void printX() {}
template<typename T, typename... Type>
void printX(const T& firstArg, const Type&... args) 
{
  cout << firstArg << endl; // 打印第一个参数
  printX(args...);  
}
printX(7.5,"hello",bitset<16>(377),42); // 此时如果执行这一行  
// 就相当对第8行的print函数中的参数,逐一使用函数模板,结果就输出
// 第8行函数中的所有参数

例2:使用这个特性重写printf库函数。

void printf(const char *s)
{
  while (*s) {
    if (*s == '%' && *(s++) == '%') {
      throw std::runtime_error("invalid format string: missing");
    std::cout << *s++;
    }
  }
}
template<typename T, typename... Args>
void printf(const char *s, T value, Args... args)
{
  while (*s) {
    if (*s == '%' && *(s++) == '%') {
      std::cout << value;
      printf(++s, args...); // call even *s == 0
      return;
    }
  }
}

例3:实现maximun,一些值的最大值。

注:用初始化列表也可以,所以只是实例,不是说这个例子是最佳实例。

cout << max({1, 2, 3 4}) << endl;

例4:实现maximun,一些值的最大值

cout << maximum(57, 48, 60, 100, 20, 18) << endl;
int maximum(int n) { return n; }
template<typename... Args>
int maximum(int n, Args... args)
{
  return std::max(n, maximum(args...));
}

例5:使用类模板(用不同一般的方式使用first元素和last元素)

。。。没看明白

例6:用于递归继承的类模板

template<typename... Values> class tuple;
template<> class tuple<> {};
///////////////////////////////////////////
template<typename Head, typename... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> 
{
  typedef tuple<Tail...> inherited;
protected:
  Head m_head; // 位置在head()函数必须要前面head()函数才能用成员变量m_head
public:
  tuple(){}
  tuple(Head v, inherited(vtail...)):m_head(v), inherited(vtail..) {}
  
  // Head::type 编译不通过,不是所有类型都能::type
  // typename Head::type head() { return m_head; } 
  auto head()->decltype(m_head) { return m_head; }
  inherited& tail() { return *this; }
}
// run
tuple<int, float, string> t(41, 6.3, "nico");
cout << sizeof(t) << endl;
cout << t.head() << endl;
cout << t.tail().head() << endl;
cout << t.tail().tail().head() << endl;
// 结果是分行输出 41 6.3 nico

例7:用于复合的递归

和例6相似。

右值引用

新的类型,TR1后就有,但是大家不熟悉。

用来避免一些不必要的copy。

表达式都是右值,

不能被做赋值操作的都是右值,例如a+1就是右值, 这个代码就是不成立的a+1=a。但是:

string s1("aaa");
string s2("bbb");
s1 + s2 = s1; // just OK?!
cout << s1 << endl <<  s2 << endl;
string() = "ccc"; // OK too?!

// try complex
complex<int> c1(3, 8), c2(1, 0);
c1 + c2 = complex<int>(41, 9); // c1 + c2 could be lvalue
complex<int>() = complex<int>(3, 1); // ok too! temp obj could be assigned to sth!

尽管如此,临时对象还是不能放在左边。

待更新~

这篇关于C++11 麻辣烫的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!