c++支持面向过程编程(如c),面向对象编程(OOP)和泛型编程;
c/c++编译器比较多,window下是微软编译器cl.exe,Linux机下是GCC编译器,mac下是Clang编译器(是xcode默认编译器);可以了解一个编译器和LLVM架构的关系什么是LLVM
c++中的类是一种构造类型,可以参考一下c的结构体,但是类中进行一些扩展,类成员是变量或者是函数。通过类声明的变量叫对象。
可以类比:类是一张图纸,不占用内存空间,对象才是具体的实物,占用内存空间,图纸生成实物的过程是实例化,对象也叫一个实例(Instance),类的成员变量叫属性(Property),类的成员函数叫方法(Method)
什么是面向对象编程:
先说一下面向过程:它是一个步骤化的过程,是通过函数一步一步实现这些功能,依此调用;
而什么是面向对象:是一个行为化的过程,把整个需求按照特点功能划分,将具有共性的部分抽象成类。
举个例子:
比如设计一个点餐系统,如果是面向过程,会有①客户点单给服务员;②服务员下单;③客户支付等一系列操作,然后还有其他客户下单等;
而面向对象就会将事物抽象成各种类
优点:面向过程不易于维护,复用和扩展,而面向对象易维护复用和扩展。面向对象由封装、继承、多态特性,设计出的东西低耦合、更灵活。
c语言中,每个功能的代码都封装成一个函数,多个函数放在一个源文件,而C++中多了一层封装,就是类,类中关联了相关的变量和函数,更抽象,耦合度更低,更方便我们组织和管理代码,快速梳理编程思路。
Linux中编译链接c++方式:
gcc main.cpp -lstdc++ //方式一 g++ main.cpp -o demo //方式二,指定可执行文件名称(可忽略)
为了解决合作开发时的命名冲突问题,C++ 引入了命名空间(Namespace)的概念
namespace Li{ //小李的变量定义 FILE fp = NULL; } namespace Han{ //小韩的变量定义 FILE fp = NULL } Li::fp = fopen("one.txt", "r"); //使用小李定义的变量 fp Han::fp = fopen("two.txt", "rb+"); //使用小韩定义的变量 fp
其中::
是一个新符号,称为域解析操作符,在C++中用来指明要使用的命名空间
除了直接使用域解析操作符,还可以采用 using 关键字声明
方式一: using Li::fp;//出现fp都是Li这个空间的 fp = fopen("one.txt", "r"); //使用小李定义的变量 fp Han :: fp = fopen("two.txt", "rb+"); //使用小韩定义的变量 fp 方式二: using namespace Li;//如果有未具体指定命名空间的变量产生了命名冲突,那么默认采用命名空间 Li 中的变量。 fp = fopen("one.txt", "r"); //使用小李定义的变量 fp Han::fp = fopen("two.txt", "rb+"); //使用小韩定义的变量 fp
可以发现,对于不带.h
的头文件,所有的符号都位于命名空间 std 中,使用时需要声明命名空间 std;对于带.h
的头文件,没有使用任何命名空间,所有符号都位于全局作用域。这也是 C++ 标准所规定的。
将 std 直接声明在所有函数外部,这样虽然使用方便,但在中大型项目开发中是不被推荐的,这样做增加了命名冲突的风险,推荐在函数内部声明 std。
cout 和 cin 都是 C++ 的内置对象,而不是关键字。C++ 库定义了大量的类(Class),程序员可以使用它们来创建对象,cout 和 cin 就分别是 ostream 和 istream 类的对象,只不过它们是由标准库的开发者提前创建好的,可以直接拿来使用。这种在 C++ 中提前创建好的对象称为内置对象。
介绍一下c++的bool和BOOL:
bool:布尔类型,true(1)和false(0),只占用一个字节
BOOL:int类型,true(非0)和false(0),占用大小按具体环境
在C语言中,const 用来限制一个变量,表示这个变量不能被修改,我们通常称这样的变量为常量(Constant),c++多了两个特性
① C语言对 const 的处理和普通变量一样,会到内存中读取数据;C++ 对 const 的处理更像是编译时期的#define
,是一个值替换的过程,不会去内存中读取值
int main(){ const int n = 10; int *p = (int*)&n; //必须强制类型转换 *p = 99; //修改const变量的值 printf("%d\n", n); return 0; } //像这样,c输出是99,c++也可以获取n的地址,但是输出还是10;
② C和C++中全局 const 变量的作用域相同,都是当前文件,不同的是它们的可见范围:C语言中 const 全局变量的可见范围是整个程序,在其他文件中使用 extern 声明后就可以使用;而C++中 const 全局变量的可见范围仅限于当前文件,在其他文件中不可见,所以它可以定义在头文件中,多次引入后也不会出错
在C++中,malloc和free这两个函数仍然可以使用,但是C++又新增了两个关键字,new 和 delete:new 用来动态分配内存,delete 用来释放内存。new 也是在堆区分配内存,必须手动释放,否则只能等到程序运行结束由操作系统回收,不要和C语言中 malloc()、free() 一起混用
new和delete的底层实现
int *p = (int*) malloc( sizeof(int) * 10 ); //分配10个int型的内存空间 int *p = new int[10]; //分配10个int型的内存空间,new 操作符会根据后面的数据类型来推断所需空间的大小
函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。
为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开(替换)。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function)。又称内嵌函数或者内置函数,一般只将那些短小的、频繁调用的函数声明为内联函数(编译后体积小,而且如果采用调用函数的话时空开销比函数代码还大)
要在函数定义处添加 inline 关键字,而且一般直接在声明处定义;
#include <iostream> using namespace std; //内联函数,交换两个数的值 inline void swap(int *a, int *b){ int temp; temp = *a; *a = *b; *b = temp; } int main(){ int m, n; cin>>m>>n; cout<<m<<", "<<n<<endl; swap(&m, &n); cout<<m<<", "<<n<<endl; return 0; }
内联函数主要有两个作用,一是消除函数调用时的开销,二是取代带参数的宏
因为宏只是字符替换,带参数会出现意想不到的错误;
#include <iostream> using namespace std; #define SQ(y) y*y int main(){ int n, sq; cin>>n; sq = SQ(n+1); cout<<sq<<endl; return 0; //输入:9 输出结果:19 }
sq = SQ(n+1);在宏展开后会变为
sq = n+1*n+1,字符直接替换成9;
还要看如何规范使用内联函数,看内存问题和头文件问题;
C++规定,默认参数只能放在形参列表的最后,而且一旦为某个形参指定了默认值,那么它后面的所有形参都必须有默认值。
void func(int a, int b=10, int c=20){ } void func(int a, int b, int c=20){ } //错误例子: void func(int a, int b=10, int c=20, int d){ } void func(int a, int b=10, int c, int d=20){ } //在同一文件中可以多次函数声明,但在给定的作用域中一个形参只能被赋予一次默认参数,而且要赋值的参数右侧都有默认值 //多次声明同一个函数 void func(int a, int b, int c = 36); void func(int a, int b = 5, int c);
C++ 规定,在给定的作用域中只能指定一次默认参数,所以同一文件中默认参数声明和定义不能同时带有同一个位置的默认参数;
C++ 允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是函数的重载(Function Overloading);
参数列表又叫参数签名,包括参数的类型、参数的个数和参数的顺序,只要有一个不同就叫做参数列表不同,但是只是参数名称不同不可以叫函数重载,仅仅返回类型不同也不可以;
C++代码在编译时会根据参数列表对函数进行重命名,例如void Swap(int a, int b)
会被重命名为_Swap_int_int
,void Swap(float x, float y)
会被重命名为_Swap_float_float
。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做重载决议(Overload Resolution)
本质上:它们还是不同的函数,占用不同的内存,入口地址也不一样;
重载的二义性问题