<<现代C++实战30讲>>打卡学习笔记—提高篇

2020-02-15  本文已影响0人  IIGEOywq

说明

提高篇11讲主要学习泛型编程、面向对象编程、元模版编程、函数式编程、并发。
每日打卡更新。

打卡Day10:到底应不应该返回对象?

#include <iostream>
#include <stdlib.h>
using namespace std;
// Can copy and move
class A {
 public:
  A() { cout << "Create A\n"; }
  ~A() { cout << "Destroy A\n"; }
  A(const A&) { cout << "Copy A\n"; }
  A(A&&) { cout << "Move A\n"; }
};
A getA_unnamed() {
  A a;
  return a;
}
A getA_duang()
{
  A a1;
  A a2;
  if (rand() > 42) {
    return a1;
  } else {
    return a2;
  }
}

int main() {
  // setA();
  //返回值优化
  //auto a = getA_unnamed();
  auto a = getA_duang();
}

输出结果

Create A
Create A
#返回a1时,调用移动函数
Move A
Destroy A
Destroy A
Destroy A

打卡Day11:Unicode:进入多文字支持的世界

字符 ASCII Unicode UTF-8
A 01000001 00000000 01000001 01000001
无法编码 01001110 00101101 11100100 10111000 10101101

4.2 BOM的作用:Unicode实现的标准太多,为了区分,通常在Unicode 文本文件头加一个字符 U+FEFF,这个字符就是 BOM(byte order mark)字符的约定,根据这个字符区分是UTF-32 编码,还是UTF-16等。

打卡Day12:编译期多态:泛型编程和模板入门

如果有Java基础,这一章节是比较好理解的。

#include <iostream>
using namespace std;
//定义函数模版
template <typename E>
E my_gcd(E a, E b) {
  while (b != E(0)) {
    E r = a % b;
    a = b;
    b = r;
  }
  return a;
}
int main() {
  //如果使用cl_I类型(开源库定义的cl_I
  //高精度整数类型,不支持求余运算),就会报错 因此可以重载my_gcd方法
  cout << my_gcd<>(3, 4) << endl;
}

我前面描述了面向对象的“动态”多态,也描述了 C++ 里基于泛型编程的“静态”多态。需要看到的是,两者解决的实际上是不太一样的问题。“动态”多态解决的是运行时的行为变化——就如我前面提到的,选择了一个形状之后,再选择在某个地方绘制这个形状——这个是无法在编译时确定的。“静态”多态或者“泛型”——解决的是很不同的问题,让适用于不同类型的“同构”算法可以用同一套代码来实现,实际上强调的是对代码的复用。C++ 里提供了很多标准算法,都一样只作出了基本的约定,然后对任何满足约定的类型都可以工作

打卡Day13:编译期能做些什么?一个完整的计算世界

int factorial(int n) 
{
    //控制语句元函数不能使用
    if (n == 0)
       return 1;
    return n * factorial(n - 1);
}

void foo()
{ 
    //运行时执行
    int x = factorial(4); // == (4 * 3 * 2 * 1 * 1) == 24
    int y = factorial(0); // == 0! == 1
}

模版元编程

#include <iostream>
template <int N>
struct Factorial {
  //枚举是元编程可以使用的数据
  enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0> {
  enum { value = 1 };
};
// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo() {
  //编译时执行
  int x = Factorial<4>::value;  // == 24
  std::cout << x << std::endl;
  int y = Factorial<0>::value;  // == 1
  std::cout << y << std::endl;
}
int main() { foo(); }

打卡Day14 SFINAE:不是错误的替换失败是怎么回事?

这一讲的内容比较晦涩,后面还要在看几遍

#include <stdio.h>
struct Test {
  typedef int foo;
};
template <typename T>
void f(typename T::foo) {
  puts("1");
}
template <typename T>
void f(T) {
  puts("2");
}

int main() {
  f<Test>(10);  //两个重载函数f(Test::foo) 和 f(Test),实际匹配到f(Test::foo)
  f<int>(10);  //两个重载函数f(int::foo) 和 f(int),实际匹配f(int)
}

下面是一段其他同学的学习留言:

我自己在写数据序列化为json文本的时候,就遇到了这样头疼的问题:如何根据类型,去调用对应的函数。
如果是简单的int,bool,float,直接特化就好了。
如果是自定义的结构体呢?我的做法就是判断自定义结构体中是否有serializable和deserializable函数,就用到了文中最开始的方法判断。
然而那会儿我写得还是太简单粗暴,在代码中用的是if去判断,对于不支持的类型,直接报错,并不能做到忽略。

打卡Day15 constexpr:一个常态的世界

constexpr int factorial(int n)
{
  if (n == 0) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}
int main()
{
  constexpr int n = factorial(10);
  printf("%d\n", n);
}

打卡Day16 函数对象和lambda:进入函数式编程

这一讲的内容和Java语言中的内容类似;

#include <array>  // std::array
#include <iostream>
#include <iostream>  // std::cout/endl
#include <numeric>   // std::accumulate

using namespace std;
struct adder {
  adder(int n) : n_(n) { std::cout << n << std::endl; }
  //定义函数调用运算符
  int operator()(int x) const {
    std::cout << x << std::endl;
    return x + n_;
  }

 private:
  int n_;
};

int add_2(int x) {
  std::cout << x << std::endl;
  return x + 2;
};

template <typename T>
auto test1(T fn) {
  return fn(2);
}

int main() {
  //定义了函数调用运算符的类对象称为函数对象
  auto add = adder(1);
  add(2);
  //传递给一个函数的实参称为函数指针或者引用
  test1(add_2);
  // Lambda 表达式(以一对中括号开始)
  auto add_3 = [](int x) {
    std::cout << x << std::endl;
    return x + 2;
  };
  add_3(3);
  //泛型Lambda 表达式
  array ary{1, 2, 3, 4, 5};
  auto s = accumulate(ary.begin(), ary.end(), 0,
                      [](auto x, auto y) { return x + y; });
  cout << s << endl;
}

打卡Day17 函数式编程:一种越来越流行的编程范式

个人对于函数式编程接触的比较多,Java8的Stream Api,RxJava的操作算子,还有搞大数据的时候,接触到的Hadoop MapReduce和Spark的各种操作算子都是经典的函数式编程。举个经典例子统计一个文本中的
例如Map将一个对象映射为另一个对象,reduce将映射后归为一个整体。参考代码

打卡Day18 应用可变模板和tuple的编译期技巧

#include <iostream>
void tprintf(const char* format) // 基础函数
{
    std::cout << format;
}

//第一个实参和剩余的实参分离开来,然后对于剩余的实参递归
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // 递归变参函数
{
    for ( ; *format != '\0'; format++ ) {
        if ( *format == '%' ) {
           std::cout << value;
           tprintf(format+1, Fargs...); // 递归调用
           return;
        }
        std::cout << *format;
    }
} 
int main()
{
    tprintf("% world% %\n","Hello",'!',123);
    return 0;
}
#include <tuple>
#include <iostream>
#include <string>
#include <stdexcept>
 
std::tuple<double, char, std::string> get_student(int id)
{
    if (id == 0) return std::make_tuple(3.8, 'A', "Lisa Simpson");
    if (id == 1) return std::make_tuple(2.9, 'C', "Milhouse Van Houten");
    if (id == 2) return std::make_tuple(1.7, 'D', "Ralph Wiggum");
    throw std::invalid_argument("id");
}
 
int main()
{
    //初始化tuple对象
    auto student0 = get_student(0);
    //通过get读取数据
    std::cout << "ID: 0, "
              << "GPA: " << std::get<0>(student0) << ", "
              << "grade: " << std::get<1>(student0) << ", "
              << "name: " << std::get<2>(student0) << '\n';

}
上一篇 下一篇

猜你喜欢

热点阅读