一、C++与C语言的区别
1.1 输入输出
- C语言输入输出数据,需要记忆对应格式字符
#include<stdio.h> int main() { int a; scanf_s("%d", &a); printf("%d", a); return 0; }
- C++自动根据输入输出的数据类型进行处理,不需要程序员记忆格式制符
#include<iostream> using namespace std; int main() { int a; cin >> a; cout << a << endl; return 0; }
C++的标准库<iostream>定义了4个IO对象:
- cin:istream对象,标准输入。
- cout:ostream对象,标准输出。
- cerr:标准错误。用来输出警告和错误信息给程序的使用者。
- clog:用来产生程序执行的一般信息。
"<<"操作符
- "<<"是预定义的插入符,作用在cout上可实现屏幕输出。
">>"操作符
- ">>"是预定义的提取符,作用在cin上可实现键盘输入。
1.2 数据类型与表达式
C++相对C语言的新增数据类型:布尔型bool,引用类型type&,类类型class
- 布尔型bool:false or true
- 引用类型type&:同一个变量空间,取个别名。
int value; int &p=value;
- 类类型class:协助程序员实现面向对象思想。
#include <iostream> using namespace std; //定义一个类 class Point { private: int x; int y; public: // 构造函数 Point(int a, int b) { x = a; y = b; } // 成员函数 void display() { cout << "x = " << x << ", y = " << y << endl; } }; int main() { // 创建一个Point对象,并传入参数 Point p(3, 4); // 调用对象的成员函数 p.display(); return 0; }
C++与C语言的类型转换规则相同:从内存小的向大的转换,低类型向高类型转换,一般没问题;高类型向低类型转换,可能会造成数据丢失。(低类型->高类型)
1.3 动态内存管理
动态内存管理用处非常大,但在计算要分配的空间大小时非常不方便。
C++提供了新的动态内存管理方式
- C语言:使用malloc进行动态内存分配,使用free进行动态内存释放。必须程序员手工计算具体要分配多大的字节空间数。
- C++:使用new和delete实现动态内存的分配和回收。不需要程序员计算分配空间大小,程序可以直接通过类型名自动计算并分配合适空间的大小。
1.3.1 动态分配和释放单个数据的存储区
#include<iostream> using namespace std; int main() { int* p; p = new int; if (p = NULL) { cout << "Allocation failure\n"; } else { *p = 15; cout << *p; delete p; } return 0; }
- 在动态分配内存的同时存入数据
int *p; p=new int(100); delete p;
- 如果p=new int(98.5),会怎么样?会出现类型转换,数据从double转为int。
- delete释放的内存空间必须是由new分配的,否则执行delete时将会导致严重的错误。(new没分配内存给p,p就是未知空间,删除未知空间很危险)
1.3.2 动态分配和释放一维数组的存储区
#include<iostream> using namespace std; int main() { int* p; p = new int[100]; if (p == NULL) { cout << "Allocation failure!\n"; } else { for (int i = 0; i < 100; i++) p[i] = i + 1; for (int j = 0; j < 100; j++) cout << p[j] << ' '; delete[]p; } return 0; }
1.3.3 动态分配和释放二维数组的存储区
1.4 函数重载
C++中的函数重载是指在同一个作用域中定义多个同名函数,但它们的参数列表不同(包括参数个数、参数类型、参数顺序等),以便根据不同的参数类型或参数个数来选择调用合适的函数。
函数重载的语法规则如下:
- 函数名相同
- 参数列表不同(包括参数个数、参数类型、参数顺序等)
- 返回值类型可以相同也可以不同
注:
- 不能以形参名字不同or函数返回类型的不同来区分函数。
- 不要将不同功能的函数定义为重载函数,以免出现混淆。
函数重载中的默认形参
- 用一个函数能够表示重载的两个函数。
int add(int x){return (x);} int add(int x,int y){return (x+y);}
int add(int x,int y=0){return(x+y);}
- 带默认形参值的函数
在定义函数时预先声明默认的形参值,调用时如果给出实参,则用实参初始化形参;否则采用预先声明的默认形参值。
int add(int x=6,int y=5){return x+y;} int main(){ int a,b,c; a=add(10,20);//a=30 b=add(10); //b=15 c=add(); //c=11 return 0; }
- 默认形参必须按照从右到左的顺序声明。在有默认值的形参右边,不能出现无默认值的形参。(不按照这种规则,在实际参数传递过程中就会出现问题)
int add(int x,int y=0){return(x+y);} (√)
int add(int x=0,int y){return(x+y);} (×)
- 在相同的作用域内,默认形参值的说明应保持唯一;但在不同的作用域内,允许说明不同的默认形参值。
1.5 内联函数
C++相比C语言,还多了一个内联函数
- 出现原因:在C++中既要运算速度快,又要有类型检查。
宏定义没有类似于普通函数调用时的系统开销,并且宏定义的参数可以适宜大多数类型的数据,达到函数重载类似的效果。但宏定义有时会产生不可预料的副作用,因为宏定义是直接替换,并不是对参数进行处理之后再计算。而且宏定义没有类型检查。
- 用inline定义内联函数
C++中的内联函数既具有宏定义的优点,又克服了宏定义的缺点。
使用方式:在函数名前加上“inline”,即内联函数。
inline void func(int a,int b);//在编译时调用func的地方用函数体进行了替换,所以程序执行时 会减少调用开销。
- 内联函数是用来提升运行效率的,只有那些频繁被调用且函数体较小的函数定义为内联函数。内联函数内不允许有循环语句和switch语句,否则按照普通函数来处理。
1.6 常量
- 使用数字常量(指在程序运行过程中,其值不能被改变的量)有两个问题:可读性差,可修改性差。
- 程序员通常用符号常量来解决数字常量的问题,有两种使用符号常量的方式:
- 宏定义:#define 符号常量名 数值
- C99的const常量:const 数据类型 符号常量名=数值;
注:在声明时一定要赋初值,而且在程序中间不能改变其值。
用"#define"和用"const"定义符号常量的本质区别:
- 用"#define"定义的符号变量只在编译时完成宏替换(简单的字符串替换),在程序运行过程中不占内存空间。
- 用"const"定义的符号常量在程序运行期间占据内存空间,只是用const来指明该内存空间的只读约束。
1.7 引用
引用type&:引用就是给一个单元起一个别名,引用与它所引用的变量共享存储单元。
引用的三种用法:
- 独立引用
在声明独立引用时,必须对它进行初始化,这种情况下的别名绑定是永久的。
初始化独立引用的几种方式:
- "="右端是一个变量
int a; int &ra=a;
- "="右端是一个常量
const float &ra=1.0;
- 定义常引用
int x=1; const int &rx=x;
- 作为函数参数
C/C++采用“传值”的方式进行传递参数,在这种情况下,实参和形参是两个不同的单元,在结合时,实参的值将会被拷贝到形参中。形参的改变不会影响到实参。
- C语言中,采用传递指针的方式,解决了形参和实参之间数据传递的问题。
int* swap(int* a, int* b); int main() { int a = 2, b = 3; swap(&a, &b); cout << a << ' ' << b; return 0; } int* swap(int* a, int* b) { int t = *a; *a = *b; *b = t; return 0; }
- C++中,采用传递引用的方式。在这种情况下,形参的名字将被看作是实参的别名,即形参就是实参本身。此时,对形参的改变也就直接改变了实参。
void swap(int& a, int& b) { int t = a; a = b; b = t; } int main() { int a = 2, b = 3; swap(a, b); cout << a << ' ' << b; return 0; }
- 作为参数返回类型
函数返回引用,实际上返回的是一个存储单元(变量),即“左值”。
如果一个函数返回引用,那么函数调用可以出现在复制号的左边。
int& f(int* pint) { return *pint; } int main() { int a = 10, b; b = f(&a) * 5; f(&a) = 88; cout << b << ' ' << a;//b=50,a=88 return 0; }