一、基础知识

Link

C++是种编译型语言。要运行一个程序,其源文本需要通过编译器处理, 生成一些目标文件,再经链接器组合后给出一个可执行文件。

ISO C++ 标准定义了两类东西:

  • 语言核心特性, 诸如内置的类型(例如 char 和 int) 以及循环(例如 for-语句 和 while-语句)
  • 标准库组件, 诸如容器(例如 vector 和 map) 以及 I/O 操作(例如 << 和 getline())。标准库组件完全是普通的 C++ 代码,由具体的 C++ 实现提供。 换句话说, C++ 标准库可以且确实是用 C++ 自身 (包括少量的机器语言代码,用于线程上下文切换等功能)实现的。

C++ 是静态类型语言。 就是说,任何一个东西(例如对象、值、名称和表达式)被用到的时候, 编译器都【必须】已经知晓其类型。对象的类型确定了可施加操作的集合。


如果在 C++ 程序里要做一件事,主要的方式是调用某个函数去执行它。 定义函数就是指定某个操作怎样被执行。 除非事先声明过,否则函数无法被调用

函数声明给出了该函数的名称、返回值类型(如果有的话)、 以及调用它时必须提供的参数数量和类型。

函数声明中可以包含参数名。 这对程序的读者有益,但除非该声明同时也是函数定义,编译器将忽略这些参数名。例如下面两种声明实质是一样的:

double sqrt(double d); // 返回 d 的平方根
double square(double); // 返回参数的平方

函数可以作为类的成员。 对于成员函数(member function)来说,类名也是该函数类型的组成部分。例如:

char& String::operator[](int index);


声明是把一个实体引入程序的语句。它规定了这个实体的类型:

  • 类型(type) 规定了一组可能的值和一组(针对对象的)运算;
  • 对象(object) 是一块内存,其中承载某种类型的值;
  • (value) 是一些二进制位,其含义由某个类型规定;
  • 变量(variable) 是一个具名对象。


C++ 有多种初始化方法,比如上面用到的 =, 还有一种通用形式,基于花括号内被隔开的初值列表:

double d1 = 2.3;// d1 初始化为 2.3
double d2 {2.3};// d2 初始化为 2.3
double d3 = {2.3};// d3 初始化为 2.3(使用 { ... } 时,此处的 = 可有可无)
complex<double> z = 1;// 一个复数,使用双精度浮点数作为标量
complex<double> z2 {d1,d2};
complex<double> z3 = {d1,d2};// (使用 { ... } 时,此处的 = 可有可无)
vector<int> v {1,2,3,4,5,6};// 一个承载 int 的 vector

没有特定原因去指明类型时,就可以用auto,“特定原因”包括:

  • 如果该定义处在较大的作用域中,希望其类型对阅读源码读的人一目了然。
  • 希望明确规定变量的取值范围或精度(比方说,想用double,而非float)。


关于不可变更,C++有两种概念:

  • const:相当于“我保证不会修改这个值”。 它主要用于指定接口,对于通过指针以及引用传入函数的数据,无需担心其被修改。 编译器为const作出的“保证”担保。一个const的值可在运行期间得出。

  • constexpr:相当于“将在编译期估值”。 它主要用于定义常量,指定该数据被置于只读内存(在这里被损坏的几率极低)中, 并且在性能方面有益。 constexpr的值必须由编译器算出

constexpr double square(double x) { return x*x; }
int var = 17;
constexpr double max1 = 1.4*square(17);// OK 1.4*square(17) 是常量表达式,可在【编译期】估值
constexpr double max2 = 1.4*square(var);// 报错:var不是常量表达式
const double max3 = 1.4*square(var);// OK,可在【运行时】估值

要成为constexpr,函数必须极其简单,且不能有副作用,且只能以传入的数据作为参数。 尤其是,它不能修改非局部变量,但里面可以有循环,以及它自己的局部变量。例如:

constexpr double nth(double x, int n) {// 假定 n>=0
    double res = 1;
    int i = 0;
    while (i<n) {
        res*=x;
        ++i;
    }
    return res;
}

在某些场合下,语言规则强制要求使用常量表达式(比如:数组界限、case标签、模板的值参数,以及用constexpr定义的常量)。其它情况下,编译期估值都侧重于性能方面。 抛开性能问题不谈,不变性(状态不可变更的对象)是一个重要的设计考量。

char v[6];  // 6个字符的数组
char* p;    // 指向字符的指针

在声明里,[]的意思是“什么什么的数组”,而*的意思是“指向什么什么东西”。

char* p = &v[3];    // p指向v的第四个元素
char x = *p;        // *p是p指向的对象

以上,v有六个元素,从v[0]到v[5]。指针变量p可持有相应类型对象的地址。

在表达式里,一元前置运算符*的意思是“什么什么的内容”, 而一元前置运算符&的意思是“什么什么的地址”。我们可以把前面初始化定义的结果图示如下:

如下示例是将数组v中的所有元素加一,若不想把v中的值复制到变量x,而是仅让x引用一个元素:

void increment() {
    int v[] = {0,1,2,3,4,5,6,7,8,9};
    for (auto& x : v)// 为v里的每个x加1
        ++x;
}

在声明中,一元前置运算符&的意思是“引用到什么什么”。 引用和指针类似,只是在访问引用指向的值时,无需前缀*。 此外,在初始化之后,引用无法再指向另一个对象

在定义函数参数时,引用就特别有价值。例如:

void sort(vector<double>& v);

通过引用,我们确保了在调用sort(my_vec)的时候,不会复制my_vec, 并且被排序的确实是my_vec,而非其副本。

想要不修改参数,同时还避免复制的开销,可以用const引用。接收const引用参数的函数很常见。

运算符(例如&*[])用在声明中的时候, 被称为声明运算符(declarator operator):

T a[n]  // T[n]: 具有n个T的数组
T* p    // T*: p是指向T的指针
T& r    // T&: r是指向T的引用
T f(A)  // T(A): f是个函数,接收一个A类型的参数,返回T类型的结果

在老式代码里,通常用0NULL,而非nullptr。 但是,采用nullptr, 可以消除整数(比如0或NULL)和指针(比如nullptr)之间的混淆。
对指针指的判定(比如if(p)),等同于将其与nullptr比较(也就是if(p!=nullptr))。


初始化和赋值不一样。 一般来说,想要让赋值操作正确运行,被赋值对象必须已经有一个值。 另一边,初始化的任务是让一块未初始化过的内存成为一个有效的对象。 对绝大多数类型来说,针对 未初始化变量 的读取和写入都是未定义的(undefined)。 对于内置类型,这在引用身上尤其明显:

int x = 7;
int& r {x}; // 把r绑定到x上(r引用向x)
r = 7;      // 不论r引用向什么,给它赋值
int& r2;    // 报错:未初始化引用
r2 = 99;    // 不论r2引用向什么,给它赋值

很幸运,不存在未初始化的引用; 如果能,那么r2=99就会把99赋值给某个不确定的内存位置; 其结果会导致故障或者崩溃。

=可用于初始化引用,但千万别被它搞糊涂了。例如:

int& r = x; // 把r绑定到x上(r引用向x)

这依然是初始化r,并把它绑定到x上,而不涉及任何的值复制操作。

初始化和赋值的区别,对很多用户定义的类型 ——比如string和vector——而言同样极度重要, 在这些类型中,被赋值的对象拥有一份资源,该资源最终将被释放。

参数传递和返回值返回的基本语义是初始化。举例来说,传引用(pass-by-reference)就是这么实现的。

忠告

  • [1] 别慌!船到桥头自然直;
  • [2] 不要专门或单独使用内置特性。 恰恰相反,基本(内置)特性,最好借助程序库间接使用, 比方说 ISO C++ 标准库(第8-15章);
  • [3] 想写出好程序,不必对C++掌握到巨细靡遗。
  • [4] 把力气用在编程技术上,别死磕语言特性。
  • [5] 有关语言定义相关问题的最终解释, 请参考 ISO C++ 标准;
  • [6] 把有用的操作“打包”成函数,再取个好名字;
  • [7] 函数应当仅具有单一的逻辑功能;
  • [8] 保持函数简短;
  • [9] 当函数针对不同类型执行同样概念的操作时,请采用重载;
  • [10] 当函数可能在编译期估值时,用constexpr声明它;
  • [11] 去理解基本语义向硬件的映射;
  • [12] 用数字分隔符为大文本值提高可读性;
  • [13] 不要使用复杂表达式;
  • [14] 不要使用导致范围缩小的类型转换;
  • [15] 尽量让变量的作用域保持最小;
  • [16] 不要使用“魔数”;使用符号常量;
  • [17] 尽量用不可变更的数据;
  • [18] 每个声明里有(且仅有)一个名称;
  • [19] 保持常见和局部名称简短,让不常见和非局部名称长一些;
  • [20] 不要使用形似的名称;
  • [21] 不要使用全大写(ALL_CAPS)名称;
  • [22] 在提及类型的声明里,尽量用{}-初始化 语法;
  • [23] 使用auto以避免重复输入类型名;
  • [24] 尽量别弄出来未初始化的变量;
  • [25] 尽量缩小作用域;
  • [26] 如果在if-语句的条件中定义变量,尽量采用针对0的隐式判定;
  • [27] 仅在涉及位操作时,使用unsigned
  • [28] 确保对指针的使用简单且直白;
  • [29] 用nullptr,而非0NULL
  • [30] 在有值去初始化它之前,别声明变量;
  • [31] 别给直观的代码写注释;
  • [32] 用注释阐释意图;
  • [33] 保持缩进风格一致。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • 抱佛脚一时爽,一直抱佛脚一直爽!这篇文章总结常见的c++面试问题~因为是抱佛脚,所以结构上没有什么逻辑...参考链...
    山幺幺阅读 713评论 0 0
  • 1.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”? 答:首先,extern是C/C...
    曾令伟阅读 919评论 0 4
  • Java概述 何为编程 编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程。 ...
    小饭_317b阅读 114评论 0 0
  • 原文链接[https://mp.weixin.qq.com/s/ZzizE5ObkY9fh3JvuxgARA] 1...
    代码的路阅读 57评论 0 2
  • 数据类型 7种基本的C++数据类型 类型关键字占用内存 sizeof(类型关键字)布尔型bool1B字符型char...
    _好好学习阅读 503评论 0 0