C++学习笔记

本文最后更新于:2019年9月4日 晚上

一、C到C++

1.2 C++开发环境搭建

1.2.1 如何选择合适的开发环境

开发环境:编辑器 + 编译器 MSVC MinGW GNU(gcc g++) Clang + 辅助(调试器、反编译、代码提示与补全等)

1.2.2.3、市面主流可选C++开发IDE

  • CodeBlocks                 小巧、开源免费、跨平台,初学可试试
  • eclipse + CDT + MinGW    推荐本来就熟悉eclipse的人偶尔开发C++使用
  • VisualStudio201X            庞大、好用、不跨平台,推荐Windows上做大项目使用
  • QtCreator                中等、开源免费、跨平台,推荐嵌入式开发者首选
  • VSCode                    扩展性强、漂亮好用、跨平台,推荐web开发者做C++

   

       

1.2.2.4、对开发环境的正确心态

(1)不要执着于新版本,也不要执迷于很多年前的老东西
(2)没有最好,甚至没有最适合,工具而已,够用即可
(3)不要产生过度依赖,实际开发中可能会需要切换

1.2.2.5、本课程开发环境选择

Windows qtcreator,

qtcreator的安装参考本网站其他文章。

使用qtcreator开发纯C++项目、基于qmake

  1. 文件 -> Non-Qt Project -> Plain C++ Application
  2. 新建项目名,选择保存路径(不能有中文),建议自己再新建一个项目文件夹,保存项目文件夹、编译输出文件夹
  3. 选择 qmake 编译系统
  4. kits 选择 MinGW
  5. summary 默认不操作

1.3 C++基础编程

1.3.1.何为命名空间

1.3.1.1、命名空间的引入

(1)命名空间namespace,是C++引入的一种解决全局变量和函数名冲突的机制

(2)C语言没有namespace,但是C++及之后的java python等都有

(3)namespace的关键点有2个:一是如何解决名称冲突,二是如何合法访问变量

1.3.1.2、C语言如何解决名称冲突

  • 同一个C文件不要太大,由一个人写;
  • 每个C文件(或几个C文件构成的一个模块)中所有全局变量和函数前加统一的唯一前缀;
  • 不需要文件外访问的全局变量和函数前面都加static
  • C语言的解决方案可行,C++早期就是这样做的,但这种方法太low(依赖程序员个人能力了和配合)

1.3.1.3、C++命名空间如何解决问题

(1)namespace关键字,定义格式为namespace xx{};

(2)一个特定名称的namespace的一对大括号内部定义的变量、函数、类等均属于该命名空间内

(3)在命名空间内部互相引用:直接使用变量名、函数名等

(4)跨命名空间互相引用时:被引用方的命名空间名+变量名函数名

(5)命名空间看起来就好像一种前缀,命名空间本质上其实是对全局变量和函数在一定范围内链接属性的更改和控制

1.3.1.4、关于语言特性的思考

(1)语言特性是语言通过关键字或符号所支持的一种功能特性,如namespace、template、运算符重载、面向对象等。

(3)语言特性越多或者设计越复杂,则语言本身就越难使用,但语言就越厉害

(4)语言特性体现为某种语法,本质上靠编译工具链提供支持

(5)C++11/14/17/20的版本变迁,无非是新增或修正某些细节语言特性

1.3.2.namespace的初级定义和使用

一般情况下,同一文件中会只使用一个 namespace

1
2
3
4
5
namespace NS1 {
void func2(void){

}
}

(1)namespace的三种引用方法

// 方式 一

NS1::func2();

// 方式二

1
2
using NS1::func2;  // 只能直接访问func2
func2();

// 方式三

1
2
using namespace NS1;  // NS1 所有均可直接访问
func2();

1.3.2.2、不同C文件间定义和使用namespace

其实不同文件内的函数也是有namespace,就是文件本身,就是默认命名空间,其它文件想要调用,就必须先声明后调用,类似与命名空间的声明。

namespace 不同的,是可以在同一文件内定义不同命名空间,区分各函数。

1
2
3
4
5
6
7
8
9
//test2.cpp
namespace NS1 {
void func2(void){

}
}

//test1.cpp
void NS2::func2();

1.3.3.2、默认命名空间

(1)又叫全局命名空间

(4)其他命名空间引用默认命名空间中的方法:::func();

Text
1
2
3
4
5
6
7
8
9
void func4()
{
}
namespace NS1 {
void func2(void){
::func4();
//func4(); // 也可用,但不推荐
}
}

1.3.4.C++匿名命名空间

1.3.4.1、std和cout的引入

(1)std是C++标准库定义好的一个namespace

(2)cout是std这个namespace下的一个标准输出工具,类似于C中的printf

(3)endl 表示另起一行,类似于 \r\n,回车+换行

(4)用法示例

Text
1
2
3
4
5
6
7
8
#include <iostream>

//usning namespace std;
int main(void)
{
//cout << "hello world." << endl; //
std::cout << "hello world." << std::endl;
}

1.3.4.2、匿名命名空间的定义和使用

(1)定义

Text
1
2
3
4
5
6
7
8
9
10
11
void func4()
{
}
namespace // 空间名为空
{
void func2(void){
::func4();
//func4(); // 也可用,但不推荐
}
}

(4)其他命名空间中引用匿名命名空间中的方法

1.3.4.3、匿名命名空间的价值

(1)匿名命名空间中的符号纯文件内部使用,不需要被外部引用

(2)匿名命名空间效果类似于全局变量和函数加static,但是比C中的static使用范围广

1.3.5.嵌套命名空间

1.3.5.1、嵌套namespace的定义和使用

(1)常规定义和引用

Text
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace NS1  // 空间名为空
{
namespace NS2
{
void func2(void){
}
}
}
// 引用实例1
int main(void){
NS1::NS2::func2();
}

// 引用实例2
using namespace NS1;
int main(void){
NS2::func2();
}

(2)内部嵌套引用

Text
1
2
3
4
5
6
7
8
9
10
11
12
namespace NS1  // 空间名为空
{
void func2(void){}
namespace NS2
{
void func2(void){}
void func1(void){
func2(); // 局部和全局重名,局部优先级高,实际调用NS2里面的func2()
}
}
}

(2)外部引用嵌套命名空间内的符号

Text
1
2
3
4
5
6
7
8
9
10
11
namespace NS1  // 空间名为空
{
namespace NS2
{
void func1(void){
}
}
void func2(void){
NS2::func1(); // 从外调用内部,必须加命名空间名.NS1可以省去(同在一个NS1下)
}
}

1.3.5.2、namespace的总结

(1)记住最终目标:解决全局名称冲突,同时提供合法互相访问的机制

(5)C++的语言特性多而复杂,因此学习和使用难度高,关键在于掌握方法,从本质上学

1.3.6.C++标准库介绍

1.3.6.1、C++是C的超集

(1)一个典型C程序(后缀名.c)可以完全被视为C++程序来编译

(3)一个典型C++程序(后缀名.cpp)只能当C++程序来编译,可见C++是C的超集

(1)典型C++程序中可以支持C的形式包含C库头文件,并直接使用C库API

(2)C程序可以通过__cplusplus符号是否预定义来判断当前是gcc还是g++编译

__cplusplus的值是long int类型的,值表示当前编译程序的C++编译器的版本号。

(4)C++文件名的常用后缀:源文件(.cpp .cxx .cc .c .c++),头文件(.hpp .hxx .h)

(2)C++更建议的头文件包含形式不是<stdio.h>这样,而是这样

要点:C++的标准库的头文件是没有后缀名的

1.3.6.3、C++标准库介绍

(1)C标准库即为C++标准库的一部分,完全继承并以C++方式重写,位于std命名空间中

(2)C++面向对象库,如string、iostream等,位于std命名空间中

(3)C++ STL标准模板库,如vector、map等,位于std命名空间中

1.3.6.4、C++标准库的地位和学习安排

(1)C++比C在实际工作中更依赖于库,所以学好C++标准库很重要,C++标准库蕴含了C++的各种语言特性的典型用法,学标准库就顺便学好了C++

(5)STL部分是重难点,在《朱老师C++第3部分-STL等高阶话题》中再讲

1.3.7_8.iostream的cout使用1_2

1.3.7.1、基本使用

(1)cout即标准输出,对应stdout,定义在std命名空间中

(4)cout涉及的头文件有 、(输出格式化、左右对齐)、<bits/ios_base.h>

(5)cout本质上是ostream(iostream的派生类)的一个对象

(6)流操作符<<本质上是左移运算符在iostream中的运算符重载

(2)cout现在主要用来输出调试信息,掌握主要用法和细节查询即可,不必去记。

1.3.9.iostream的cin使用

1.3.9.1、基本使用

1.3.9.2、注意点

(1)输入的时候不要使用引用符& scanf(“%d”, &val);

(2)cin的输入会以空格为中断

1.3.10.C++用fstream读写文件

1.3.10.2、C++标准库查询参考

(1)man手册:https://blog.csdn.net/u012675539/article/details/50257343

(2)http://www.cplusplus.com/reference/

(3)https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5

1.3.10.3、fstream使用举例

(1)打开/创建文件,并写入内容,保存关闭

(2)打开文件,并读取内容显示,最后关闭。

1.3.12.C++字符串string类使用

1.3.12.1、C++式字符串的使用

(1)参考手册文档

(2)代码实践

1.3.12.2、C++字符串和C字符串的对比

(1)C语言严格说没有字符串的概念,C字符串其实就是字符数组或字符指针

(2)C++和之后的java等都有字符串,本质是一个class

(3)C++字符串的优势是标准库自带可用于字符串的各种处理算法和方法

(4)C++实际开发中建议使用C++字符串而不是沿用C式字符串

1.3.13.C与C++混合编程1

1.3.13.1、为什么需要混合编程

(1)C有很多优秀成熟项目和库,丢了可惜,重写没必要,C++程序里要调用

(2)庞大项目划分后一部分适合用C,一部分适合用C++

(3)其他情况,如项目组一部分人习惯用C,一部分习惯用C++

1.3.13.2、为什么不同语言可以混合编程

(1)程序编译过程:源文件->目标(库)文件->可执行程序->镜像文件

(2)任何编程语言执行时都必须是可执行程序,所以都必须先被编译成目标文件

(3)混合编程的“混合”操作发生在链接这一步

1.3.13.3、C++和C混合编程的困难所在

(1)C++和C都是编译型语言,互相混合相对容易

(2)难点:C++支持函数名重载,而C不支持,因此编译器生成目标文件时,函数名在目标文件中的临时内部名称规则不同。导致链接时符号对不上

(3)解决方案:使用extern “C”{}; 让C++在对接的局部向C妥协兼容

1.3.13.3、使用objdump工具来研究函数编译后的符号

(1)写个典型的C语言库mylib.c和mylib.h,提供add和sub等几个函数

(2)使用gcc -c -o编译得到库文件,再objdump -d反汇编得到.i文件

(3)对比加不加extern “C”这2种情况下得到的.i文件的符号差异

实验第1步:证明了C语言中名称为add的函数,编译后符号表中就叫add

实验第2步:证明了C++语言中名称为add的函数,编译后符号表中叫_Z3addii

分析:同样的源码,编译后生成的二进制代码其实是一样的,所以功能其实也是一样的

所以本质上是可以混合编程的,但是生成的中间符号名称不同,所以链接器难受

实验第3步:证明了在C++的头文件中,只要把C++的函数的声明放在extern “C”{}的大括号范围之内,就可以让g++在编译这个函数时生成中间符号名时按照C的规则而不是按照C++的规则,所以这样的函数就可以和C的库进行共同链接。

1.3.14.C与C++混合编程2

1.3.14.1、C与C++混合编程的可能情况分析

(1)同一个项目全部有源码,一次编译链接。

(2)同一个项目中C是库,C++是源码,C++调用C

(3)同一个项目中C++是库,C是源码,C调用C++

1.3.14.2、第一种情况

(1)可能性1:全部使用g++编译。不推荐

(2)可能性2:在C的头文件中加extern “C”声明

1.3.14.3、第二种情况

(1)这种是最典型的常见情况

(2)通用解决方案:在C的头文件中加extern “C”声明,在C++中直接包含头文件调用即可

1.3.15.C调用C++库的方法

1.3.15.1、C调用C++的麻烦

(1)g++和gcc的编译时符号差异

(2)c++支持很多c并不支持的特性,如函数重载

(3)解决方案:添加一层封装层

1.3.15.2、代码实战:C调用C++库中的函数

(1)用cpp写一个库,mylib.cpp mylib.hpp,用g++编译成静态库

(2)objdump反编译库,查看确认符号

(3)用cpp写一个封装层,用上extern “C”,cmylib.cpp和cmylib.hpp,用g++编译成静态库

(4)objdump反编译库,查看确认符号

(5)用c写一个main.c,调用wrapper库,用gcc编译链接,运行查看结果

Text
1


C++学习笔记
http://lonlypan.com/2019/09/04/C++学习笔记/
作者
LonlyPan
发布于
2019年9月4日
许可协议