C++ 之 基础回顾(一)

https://www.cnblogs.com/xinxue/p/5904788.html

1  exe 程序

1.1 最小 cpp

int main(){}    // minimal cpp

  思考一:  为什么最小程序没有 return 语句?

1.2  Hello

#include <iostream>

int main()
{
    std::cout << "Hello!\n";
}

  思考二:  \n 和 std::endl 的区别是什么?

1.3  函数

复制代码

#include <iostream>

double Square(double x)
{
    return x*x;
}

void PrintSquare(double x)
{
    std::cout << "The square of " << x << " is "<< Square(x) << '\n';
}

int main()
{
    PrintSquare(1.21);
}

复制代码

   思考三:  Square 有必要写成模板函数么?

 

2  类型、变量和运算

2.1  定义

  声明 (declaration): a statement that introduces a name into the program. It specifies a type for the named entity

  类型 (type): (为对象) 定义了一系列可能 值(value) 和一系列 操作(operations)

  (value): a set of bits interpreted according to a type.

2.2  对象和变量

  对象 (object):  some memory that holds a value of some type

  变量 (variable):  a named object

2.3  初始化

  等号 "=" 属于 C 语言, C++ 中尽量用 {}-list

复制代码

double d1 = 2.3;
double d2 {2.3};

complex<double> z = 1;      // a complex number with double-precision floating-point scalars 
complex<double> z2 {d1,d2};
complex<double> z3 = {1,2};    // the = is optional with {...} 

vector<int> v {1,2,3,4,5,6};  // a vector of ints

复制代码

  c++11 的 auto

auto b = true;     // a bool
auto ch = 'x';     // a char
auto i = 123;      // an int
auto d = 1.2;      // a double
auto z = sqrt(y);  // z has the type of whatever sqrt(y) returns

  尽量使用 auto,除非如下:

  1) in a large scope where we want to make the type clearly visible to readers of our code.

  2) be explicit about a variable’s range or precision (e.g., double rather than float ).

 

3  常量

3.1 const 和 constexpr

  const,意思是 “承诺不改变其值”,常用来指定 “接口” (interfaces), 这样 数据 可以传递给 函数 而本身不被修改,编译器通过 const 关键字来保证这种承诺的执行。

const int dmv = 17;    // dmv is a named constant
int var = 17;          // var is not a constant

constexpr double max1 = 1.4∗Square(dmv);   // OK if square(17) is a constant expression
constexpr double max2 = 1.4∗Square(var);   // error : var is not a constant expression
const double max3 = 1.4∗Square(var);       // OK, may be evaluated at run time

  constexpr :meaning roughly ‘‘to be evaluated at compile time’’. This is used primarily to specify constants, to allow placement of data in memory where it is unlikely to be corrupted, and for performance.

double sum(const vector<double>&);    // sum will not modify its argument
vector<double> v {1.2, 3.4, 4.5};     // v is not a constant
const double s1 = sum(v);             // OK: evaluated at run time
constexpr double s2 = sum(v);         // error : sum(v) not constant expression

3.2  constexpr function

  For a function to be usable in a constant expression, that is, in an expression that will be evaluated by the compiler, it must be defined constexpr

constexpr double Square(double x) { return x∗x; }

  To be constexpr , a function must be rather simple: just a return -statement computing a value.

  A constexpr function can be used for non-constant arguments, but when that is done the result is not a constant expression.

 

4  控制语句

4.1  if 语句

复制代码

bool Accept()
{
    cout << "Do you want to proceed (y or n)?\n";    // write question

    char answer = 0;
    cin >> answer;                                  // read answer

    if(answer == 'y') return true;
    return false;
}

复制代码

4.2  switch 语句

复制代码

bool Accept2()
{
    cout << "Do you want to proceed (y or n)?\n";    // write question

    char answer = 0;
    cin >> answer;                                  // read answer

    switch(answer) {
    case 'y':
        return true;
    case 'n':
        return false;
    default:
        cout << "I will take that for a no.\n";
        return false;
    }
}

复制代码

4.3  while 语句

复制代码

bool Accept3()
{
    int tries = 1;
    while(tries < 4){
        cout << "Do you want to proceed (y or n)?\n";    // write question
        char answer = 0;
        cin >> answer;                                  // read answer

        switch(answer) {
        case 'y':
            return true;
        case 'n':
            return false;
        default:
            cout << "Sorry, I don't understand that.\n";
            ++tries;        // increment
        }
    }
    cout << "I'll take that for a no.\n";
    return false;
}

复制代码

 

5  指针和数组

5.1  [] 和 *

  [ ] 等于 ‘‘array of’’, ∗ 意思是 ‘‘pointer to’’

char v[6];   // array of 6 characters
char∗ p;     // pointer to character

  前缀 ∗ 是取内容, 前缀 & 是取地址

char∗ p = &v[3];     // p points to v’s fourth element
char x = ∗p;         // *p is the object that p points to

  

T a[n];   // T[n]: array of n Ts
T∗ p;    // T*: pointer to T
T& r;    // T&: reference to T
T f(A);   // T(A): function taking an argument of type A returning a result of type T

5.2  拷贝和输出

  拷贝一个数组的元素到另一个数组

复制代码

void CopyFct()
{
    int v1[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int v2[10];

    for(auto i=0; i!=10; ++i)     // copy elements
        v2[i] = v1[i];
}

复制代码

  将 v 中每一个元素的值 , 拷贝给 x 并显示其输出。

复制代码

void Print()
{
    int v[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    for(auto x : v)          // range-for 的形式一
        std::cout << x << '\n';   // for every element of v, from the first to the last, place a copy in x and print it

    for(auto x : {21, 32, 43, 54, 65})
        std::cout << x << '\n';
}

复制代码

5.3  引用

复制代码

void Increment()
{
    int v[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    for(auto &x : v)  // range-for 的形式二
        ++x;
}

复制代码

  后缀 & 意思是 ‘‘reference to’’,类似指针,但却不需要一个前缀 ∗ 来访问值

复制代码

// count the number of occurrences of x in p[]
// p is assumed to point to a zero-ter minated array of char (or to nothing)
int CountX(char* p, char x)
{
    if(p==nullptr) return 0;
    int count = 0;
    for(; *p!=0; ++p)
        if(*p == x)
            ++count;

    return count;
}

复制代码

  其中,nullptr 参见博文 C++11 之 nullptr

 

C++11 之 并发编程 (一)

  未来芯片制造,如果突破不了 5nm 极限,则 CPU 性能的提升,可能会依赖于三维集成技术,将多个 CPU 核集成在一起,使得多核系统越来越普遍。

  以前的 C++ 多线程,一是受限于平台,多借助于封装好的 APIs 来完成,例如:POSIX threads,Windows threads 等;二是受限于单核系统,本质上都是“伪多线程”:通过线程调度,使得单核系统进行任务的切换,形成多线程的假象。

  新的 C++11 标准,在语言层面上实现了多线程,其库中提供了相关组件,使得跨平台编写多线程 cpp 程序成为可能,最终能够在多核系统中实现真正的并行计算

1  并发 (concurrency)

1.1 并发与并行

  计算机中的并发,指的是在单一系统中,同时执行多个独立的活动。对于多核系统,它们在同一时刻进行的活动,则称为并行

  通俗的理解:当有两个人都在边吃饭边看电视时,对于一个人而言,吃饭和看电视就是并发,对于两个人而言,他们在同一时刻进行的活动,可称为并行。

 1) 单核的任务切换 (task switching)

 

 2) 双核的并行执行 (parallel execution)

  

 1.2 线程与进程

  如果将进程比作一套房子,那么住在房子里的人,其从事的各种活动 (比如,厨房做饭,客厅吃饭,卧室看电视),就是一个个的线程。现在又搬来一个人,则当两个人都在房子里,做着各自的活动时,程序便由以前的单线程变为了多线程

  有的房间 (比如客厅) 两个人都可以同时进出,这代表着进程中某些内存空间是共享的,每个线程都可以使用这些共享内存。有的房间 (比如厕所) 一次只能容纳一个人,先进去的把门锁上,后到的人看到锁,就在外面等待,直到先进去的人把锁打开,这就是“互斥核” (mutex)

  在应用程序中,具体利用到并发编程的,一是多进程并发,二是多线程并发,如下图:   

           

          (1) 多进程并发                     (2) 多线程并发

 

2  程序示例

  实现多线程需要 1) 头文件 <thread>   2) 单独的线程函数 threadFunc()   3)线程对象 thread t(threadFunc)   4)等待线程 join()

复制代码

#include <thread>
#include <iostream>

void threadFunc()
{
    std::cout << "This is a thread!" << std::endl;
}

int main()
{
    std::thread t(threadFunc);
    t.join();
    
    std::cout << "This is main program!" << std::endl;
    
    return 0;
}

复制代码

  输出结果为:

This is a thread!
This is main program!

   当使用 t.detach() 来代替 t.join() 时,主线程 main 不会等待新线程 t(threadFunc),只会独自运行到程序结束。

 

3  任务代替线程

3.1  两个问题

1) 线程耗尽 (exhaustion)

 软件线程是一种有限的资源,当创建的线程数量多于系统所能够提供的,一个异常 std::system_error 就会抛出,程序便会终止。

2) 线程超额 (oversubscription)

 当等待运行的线程 (ready-to-run) 多于系统硬件线程 (hardware threads) 时,线程调度器会为每个软件线程在硬件线程上分配时间片 (time-slice)。若一个线程的时间片结束,另一个线程的时间片刚开始时,上下文的切换 (context switch) 就会被执行。对于多核系统,当线程从一个 CPU 被切换到另一个 CPU 中时,会造成很大的资源消耗。

3.2  基于任务 (task-based)

  基于线程编程 (thread-based),必须手动管理上面的两个问题,增加了编程的难度。

  基于任务编程 (task-based),通过 std::async,可将问题交由 C++标准库处理,简化了程序。

  1)  头文件 <future> 

  2) 单独的任务函数 taskFunc1 和 taskFunc2 

  3) 异步任务对象 auto fut1 = std::async(taskFunc1) 

  4) 获取任务函数返回值 fut1.get()

复制代码

#include <iostream>
#include <thread>
#include <future>

std::string taskFunc1()
{
    std::string str = "This is task1";
    return str;
}

std::string taskFunc2()
{
    std::string str = " and task2";
    return str;
}

int main()
{
    auto fut1 = std::async(taskFunc1);
    auto fut2 = std::async(taskFunc2);

    std::cout << fut1.get() + fut2.get() << std::endl << "This is main program" << std::endl;

    return 0;
}

复制代码

   输出结果为:

This is task1 and task2
This is main program

 

小结:

 1) thread-based programming needs manual management of thread exhaustion, oversubscription, load balancing, and adaptation to new platforms.

 2) task-based programming handles most of these issues via std::async with the default launch policy

 

参考资料:

  <C++ Concurrency in Action> chapter 1

  <Effecctive Modern C++>  chapter 7

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值