跳转至

C++ 笔记 | 第1课 C++ 的几个基本问题

C++ 历史

1979 年, 丹麦人 Bjarne Stroustrup 进入美国 AT&T 公司 Bell 实验室, 开始在 C 语言的基础上研制 C++, 1983 年研制出了 C++ 的雏形.

最初 1980 年被称为: 带类的 C(C with Class),1983 年正式命名为 C++. 从 1989 年开始 C++ 的标准化工作,1994 年推出了 ANSI C++ 标准

2011 年 9 月 ISO 批准的最新 C++ 标准是: C++11 (ISO/IEC 14882:2011)

C++ 简介

C++ 被称为面向对象的程序设计语言 OOPL (Object-Oriented Programming Language)

C 是 C++ 的子集, C++ 保持了 C 的原始思想.

C++ 在 C 的关键字的基础上增加了: catch,class, const, delete, friend, inline, new, operator, private, protected, public, template, this, throw, try, typeid, virtual, volatile 等关键字.

C++ 在 C 语言中增加的最重要的机制有三个:

  • 类 (class)
  • 函数重载 (function overloading)
  • 操作符重载 (operator overloading)

基本数据类型

新类型:bool(布尔量) 取值只有两个: true/false

  • bool 赋值别的数, 都将强制转换成 true(不为 0 时) 或 false(为 0 时)
  • bool 型占 1 个字节, 等同于 char

输入 / 输出

C++
1
2
#include <iostream>
using namespace std;

原来 C 语言的 stdio.h, string.h, math.h 等文件引用仍可使用, 但需要去掉文件名后缀.h , 前面加上 c:

C++
1
2
#include <cstring>
#include <cmath>

对于一般的输入输出操作上面两种写法没有本质区别, 但文件操作将会有较大差别.

常用的 I/O 流类库操纵符 (函数)

  • setw(int) 设置随后 (一个) 输出内容的场宽, 不够自动突破
  • setprecision(int) 设置随后输出的 (一个) 浮点数的位数 (不包括小数点)——注意:是总位数
  • hex 随后所有的整型数值采用十六进制输出
  • oct 随后所有的整数值采用八进制输出
  • dec 随后所有的整数值采用十进制输出
  • endl 回车符

要想使用 setwsetprecision, 还必须引用 iomanip.h 程序开始处加:#include <iomanip.h>#include <iomanip>

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <iostream> 
#include <iomanip> 
using namespace std;
void main(void)
{
int i=33, j=35, k=12345; float f=3.14159f;
cout << setw(10) << i << setw(8)<< j <<endl;
cout << setw(3) << k <<endl; // 场宽不足, 自动突破
cout << hex << i <<setw(3) << j <<endl; // 用十六进制输出整数 
cout << i <<setw(3) <<j <<endl;
cout << oct << i <<setw(j/8) << j <<endl; // 用八进制输出整数
cout << setw(12) << setprecision(5) << f << endl;
cout << setw(4)<<setprecision(6)<<f<<endl; // 场宽不足, 自动突破
cout << setprecision(4) << f << endl; // 未定场宽, 按照实际宽度输出 
cout << setprecision(1) << f << endl;
cout << setprecision(2) << 314.159 << endl; }
C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 运行结果
        33      35
12345
21 23
21 23
41  43
      3.1416 // 这里总位数 5 位
3.14159
3.142
3 // 保留到一位, 相当于取整(只针对 0.0~9.9)
3.1e+02
C++
1
2
3
4
5
6
7
8
9
char s1[20], s2[20], s3[20];
cin >> s1 >> s2 >> s3;
// 等同于 scanf("%s%s%s", s1, s2, s3);
cout << s1 << s2 << s3 << endl;
// 若从键盘上输入:How are you?
// 输出结果是: Howareyou? 整行输入字符串:
cin.getline(s1, N, 结束符); // '\n' 为默认结束符, 这里可以缺省参数
cin.get(s2, N, 结束符); // 一次连续读入多个 (包括空格) 字符, 直到读满 N-1 个, 或遇到 结束符
// getline 读取但不存结束符,get 既不读取也不存结束符

内联函数

为提高执行效率,C++ 增加了内联函数. 与宏定义 define 类似, 编译器将出现函数名的位置用函数体代码进行替换, 即所谓在线化, 这样省去了函数调用的参数入栈、退栈、跳转等所花费的时间. 内联函数比 define 方便, 功能也更强, 不只是简单的字符串的替换, 全面考虑了类型匹配问题.

声明为内联函数:在函数前加 inline

内联函数的书写有一些限制 (但没有统一的明确规定, 由各家的编译器的聪明程度决定):

  • 内联函数中可以定义变量, 可以使用循环语句、分支语句等;
  • 但内联函数不能直接递归或间接递归;
  • 其它使编译器不能把函数调用在线化的情形.

凡是编译器没法把函数调用在线化的内联函数, 编译器都会自动忽略 inline, 把它当做一般函数来调用,inline 也就自动失效了.

动态内存分配

C++ 用 newdelete 动态分配和释放内存, 等同于 C 的 mallocfree 函数, 但 newdelete 功能更强大

C++
1
2
3
4
5
6
7
8
int *p=new int(10); // 赋值
delete p;
int *pt=new int[10]; // 数组
delete []pt;
int (*ptt)[10]=new int[8][10]; // 分配 80 个 int 单元 // pt 是一个二维动态数组,ptt[0][0]~ptt[7][9]
delete []ptt;
int **q=new int *[10];
delete []q;

引用

C++ 提供了引用机制来传递变量地址.

在函数头部要传递地址的形参名前加 &, 而函数调用方式不变. 这样在被调用函数中对声明传递变量地址的变量的赋值操作, 实际上是直接对调用函数中相应变量的操作.

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
void swap(int &a, int &b) // 引用声明传递 a,b 变量的地址 
{ 
    int t=a;
    a=b; 
    b=t; 
}
void main()
{ 
    int i=3, j=5;
    swap(i, j); // 交换 i, j 的值
    cout << i <<'\t'<< j<<endl ;
}
// 输出结果 : 5  3 
// a 和 b 的值已经交换

引用方式 (传变量地址) 除了能简化程序书写, 还能提高程序的执行效率, 因为当传递的是结构体 (对象) 时, 只需传递其地址, 让函数直接访问, 而不必按照 C 语言传值的方式将结构体 (对象) 复制到函数的形参表中.

后面会看到: 对于类的复制构造函数来说, 这种引用方式 (传变量地址) 的机制, 不是可有可无, 而是必须的.

函数返回值也可以返回引用, 这样也只需传递所返回量的地址, 让调用函数直接访问, 而不必按照 C 语言传值的方式将返回量复制后传递返回. 但不能返回局部变量的引用.

C++
1
2
3
4
5
6
7
8
9
using namespace std;
const int &max(int &a, const int &b) 
{return a>b? a:b;// 返回的是 a 或 b 也就是 i 或 j 的地址}
void main()
{ 
    int i=3, j=5, k;
    k=max(i, j); // 取 i, j 的值
    cout << k <<endl ; 
}

引用 (reference) 是一种特殊类型的变量, 可以认为是给一个变量起别名.

C++
1
2
3
4
5
6
7
int i, j;
int &r = i; // 变量引用,r 和 i 是同一个内存单元
// r 不能再改变引用, 可以认为 r 是 i 的别名
int *p = &i;
i = 10;
j = r; // 等价于 j=i
*p = 20; // 等价于 i=20

类型修饰符 const

const 是类型修饰符, 在所定义的变量类型前面或后面出现, 使之成为不可修改的量, 即常量.

例如,int const a=1;const int a=1; 使变量 a 成为不可修改的量, 等同于 C 的常量, 但 const 比 C 语言的常量功能更强, 因为 const 是带类型的.

const 经常用于函数参数说明, 防止在函数中无意破坏参数的值或所指内容. 例如,strcpy(char *str2, const char *str1); 是将字符串 str1 复制到 str2. 将参数 str1 定义为 const, 就是防止在 strcpy 函数中破坏 str1 所指的字符串.

尤其是在 C++ 引入传地址的引用机制后,const 用来保证在函数中不会修改相应参数变量的值或所指内容

常对象

除了定义时一次性赋初值, 不能再改变它的值, 从而共用时起到保护作用.

C++
1
const STU a={"Zhang San", 'm', 2019000001};

常数据成员

C++
1
2
3
4
5
6
struct STU {
  char name[10];
  char gender;
  static int const sno = 1;  // 静态常数据成员
                             // static 与 const 同时出现
}

常指针

STU *const p=&a, 只能在初始化时赋值.

指向常对象的指针

常指针 STU *const p 和普通指针 STU *p 都不能指向常对象.

const STU *p = &a; 该指针指向 a. 该指针可以被修改

结论: 指向常对象的指针不是常指针, 可以被修改, 也可以指向普通对象.

指向常对象的常指针

const STU * const p = &a; 初始化时指向 a, 不能被修改

作用域与可见性

C++ 引入了作用域操作符 (scope operator)::, 使我们可以同时访问同名的局部变量 (max) 和全局变量 (::max)

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
const int max=10000;
void main( ) // 从键盘上读入 10 个数, 求最大值 max 输出, 但最大不超过 10000 
{ 
    int max, i,j;
    cin >> max;
    for (j=1; j<10;j++) 
    {
        cin >> i;
        if (i>max) max = i;
        if (max > ::max) max = ::max; 
    }
    cout << max<<endl; 
}
// 若 max 大于外部变量 max 的值, 则取外部常量 max 的值, 这种做法通常用来防止数组越界或溢出.

缺省参数

缺省参数指函数调用时从右边开始省略一个或多个参数.

在函数声明中可以为一个或多个参数指定缺省值, 这样函数调用时可以从右边开始省略一个或多个参数.

C++
1
2
void delay(int day=1, int month=3, int year=2019)
{............}

数据抽象

将数据类型与操作分开, 被称为数据抽象

C++
1
2
T abs(T i)
{return i<0?i:i;}

对象

将一个 (组) 变量, 以及作用于这些变量的一个 (组) 函数封装为一个单元, 这个单元就被称为对象.

类是同类对象的抽象 (共同特性的描述), 对象是类的实例化.

对象具有封闭性, 可以自己控制外界对它的访问权限, 从而严格控制外界对其中被封装变量的访问.