工作后大部分时间使用python开发,C++搁置了一段时间,最近新项目又开始使用C++。正好利用业余时间再加深对基础知识的理解。本文是学堂在线国家精品课程郑莉教授的《C++语言程序设计基础》的简要笔记。
课程地址:《C++语言程序设计基础》
随堂练习代码:
第一章 绪论
C++语言
- 是高级语言
- 支持面向对象的观点和方法
- 将客观事物看做对象
- 对象间通过消息传送进行沟通
- 支持分类和抽象
面向过程的程序设计方法:
- 机器语言、汇编语言、高级语言都支持;
- 最初的目的:用于数学计算;
- 主要工作:设计求解问题的过程。
- 大型复杂的软件难以用面向过程的方式编写
面向对象的程序设计方法:
- 由面向对象的高级语言支持;
- 一个系统由对象构成;
- 对象之间通过消息进行通信。
面向对象的基本概念
面向对象的基本概念
对象
- 一般意义上的对象:现实世界中实际存在的事物。
- 面向对象方法中的对象:程序中用来描述客观事物的实体。
抽象与分类
- 分类依据的原则——抽象;
- 抽象出同一类对象的共同属性和行为形成类;
- 类与对象是类型与实例的关系。
封装
- 隐蔽对象的内部细节;
- 对外形成一个边界;
- 只保留有限的对外接口;
- 使用方便、安全性好。
继承
- 意义在于软件复用;
- 改造、扩展已有类形成新的类。
多态
- 同样的消息作用在不同对象上,可以引起不同的行为。
程序的开发过程
程序的开发过程
程序
- 源程序:
- 用源语言写的,有待翻译的程序;
- 目标程序:
- 源程序通过翻译程序加工以后生成的机器语言程序;
- 可执行程序:
- 连接目标程序以及库中的某些文件,生成的一个可执行文件;
- 例如:Windows系统平台上的.EXE文件。
三种不同类型的翻译程序
- 汇编程序:
- 将汇编语言源程序翻译成目标程序;
- 编译程序:
- 将高级语言源程序翻译成目标程序;
- 解释程序:
- 将高级语言源程序翻译成机器指令,边翻译边执行。
C++程序的开发过程
- 算法与数据结构设计;
- 源程序编辑;
- 编译;
- 连接;
- 测试;
- 调试。
计算机中的信息与存储单位
计算机中的信息与存储单位
计算机的基本功能
- 算术运算;
- 逻辑运算。
计算机中信息:
- 控制信息——指挥计算机操作;
- 数据信息——计算机程序加工的对象。

信息的存储单位
- 位(bit,b):数据的最小单位,表示一位二进制信息;
- 字节(byte,B):八位二进制数字组成(1 byte = 8 bit);
- 千字节 1 KB = 1024 B;
- 兆字节 1 MB = 1024 K;
- 吉字节 1 GB = 1024 M。
计算机的数字系统
计算机的数字系统
计算机的数字系统
- 二进制系统;
- 基本符号:0、1。
程序中常用的数制:

R 进制转换为十进制:
各位数字与它的权相乘,其积相加,例如:
(11111111.11)2 =1×27+1×26+1×25+1×24+1×23+1×22+1×21+1×20+1×2-1+1×2-2 =(255.75)10
十进制整数转换为R 进制整数:
“除以R取余”法。
所以 6810=10001002
十进制小数→ R 进制小数:
“乘 以R 取整”法。
所以 0.312510 = 0.01012
二、八、十六进制的相互转换
- 1位八进制数相当于3位二进制数;
- 1位十六进制数相当于4位二进制数,例如:
- (1011010.10)2=(001 011 010 .100)2=(132.4)8
- (1011010.10)2=(0101 1010 .1000)2=(5A.8)16
- (F7)16=(1111 0111)2=(11110111)2
数据的编码表示
数据在计算机中的编码表示
二进制数的编码表示
- 需要解决的问题:负数如何表示?
- 最容易想到的方案:
- 0:表示“+”号;
- 1:表示“-”号。
- 原码
- “符号──绝对值”表示的编码
- 例如:

- 原码的缺点:
- 零的表示不惟一
- [+0]原 =000…0
- [-0]原 =100…0
- 进行四则运算时,符号位须单独处理,运算规则复杂。
- 零的表示不惟一
- 补码
- 符号位可作为数值参加运算;
- 减法运算可转换为加法运算;
- 0的表示唯一。
- 补码的原理
- 模数:
- n位二进制整数的模数为 2n ;
- n位二进制小数的模数为 2。
- 补数:
- 一个数减去另一个数(加一个负数),等于第一个数加第二个数的补数,例(时钟指针): 8+(-2)=8+10 ( mod 12 )=6;
- 一个二进制负数可用其模数与真值做加法 (模减去该数的绝对值) 求得其补码,例(时钟指针):-2+12=10。
- 模数:
- 补码的计算
- 借助于“反码”作为中间码;
- 负数的反码与原码有如下关系:
- 符号位不变(仍用1表示),其余各位取反(0变1,1变0),例如:
X=-1100110 [X]原 =1 1100110 [X]反 =1 0011001
- 符号位不变(仍用1表示),其余各位取反(0变1,1变0),例如:
- 正数的反码与原码表示相同,正数的补码与原码相同;
- 反码只是求补码时的中间码;
- 负数的补码由该数反码的末位加 1 求得。
- 对补码再求补即得到原码。
- 补码的优点:
- 0的表示唯一;
- 符号位可作为数值参加运算;
- 补码运算的结果仍为补码。
第二章 C++简单程序设计
符号常量
- 常量定义语句的形式为:
- const 数据类型说明符 常量名=常量值;
- 或:
- 数据类型说明符 const 常量名=常量值;
- 例如,可以定义一个代表圆周率的符号常量:
- const float PI = 3.1415926;
- 符号常量在定义时一定要初始化,在程序中间不能改变其值。
第三章 函数
内联函数
- 声明时使用关键字 inline。
- 编译时在调用处用函数体进行替换,节省了参数传递、控制转移等开销。
- 注意:
- 内联函数体内不能有循环语句和switch语句;
- 内联函数的定义必须出现在内联函数第一次被调用之前;
- 对内联函数不能进行异常接口声明。
第四章 类
委托构造函数
类中往往有多个构造函数,只是参数表和初始化列表不同,其初始化算法都是相同的,这时,为了避免代码重复,可以使用委托构造函数。
Clock类的两个构造函数:
Clock(int newH, int newM, int newS) : hour(newH),minute(newM), second(newS) { //构造函数
}
Clock::Clock(): hour(0),minute(0),second(0) { }//默认构造函数
委托构造函数
委托构造函数使用类的其他构造函数执行初始化过程
例如:
Clock(int newH, int newM, int newS): hour(newH),minute(newM), second(newS){}
Clock(): Clock(0, 0, 0) { }
复制构造函数定义
复制构造函数是一种特殊的构造函数,其形参为本类的对象引用。作用是用一个已存在的对象去初始化同类型的新对象。
class 类名 {
public :
类名(形参);//构造函数
类名(const 类名 &对象名);//复制构造函数
// ...
};
类名::类( const 类名 &对象名)//复制构造函数的实现
{ 函数体 }
复制构造函数被调用的三种情况
- 定义一个对象时,以本类另一个对象作为初始值,发生复制构造;
- 如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象,发生复制构造;
- 如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,此时发生复制构造。
第五章 数据的共享与保护
类的友元
友元是C++提供的一种破坏数据封装和数据隐藏的机制。
通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。
可以使用友元函数和友元类。
为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。
类的友元关系是单向的
如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A类的成员函数却不能访问B类的私有、保护数据。
共享数据的保护
- 对于既需要共享、又需要防止改变的数据应该声明为常类型(用const进行修饰)。
- 对于不改变对象状态的成员函数应该声明为常函数。
常类型
- 常对象:必须进行初始化,不能被更新。
- const 类名 对象名
- 常成员
- 用const进行修饰的类成员:常数据成员和常函数成员
- 常引用:被引用的对象不能被更新。
- const 类型说明符 &引用名
- 常数组:数组元素不能被更新(详见第6章)。
- 类型说明符 const 数组名[大小]…
- 常指针:指向常量的指针(详见第6章)。
常对象
用const修饰的对象
class A
{
public:
A(int i,int j) {x=i; y=j;}
...
private:
int x,y;
};
A const a(3,4); //a是常对象,不能被更新
常成员
用const修饰的对象成员
常成员函数
- 使用const关键字说明的函数。
- 常成员函数不更新对象的数据成员。
- 常成员函数说明格式:
- 类型说明符 函数名(参数表)const;
- 这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字。
- const关键字可以被用于参与对重载函数的区分
- 通过常对象只能调用它的常成员函数。
常数据成员
使用const说明的数据成员。
#include<iostream>
using namespace std;
class R {
public:
R(int r1, int r2) : r1(r1), r2(r2) { }
void print();
void print() const;
private:
int r1, r2;
};
void R::print() {
cout << r1 << ":" << r2 << endl;
}
void R::print() const {
cout << r1 << ";" << r2 << endl;
}
int main() {
R a(5,4);
a.print(); //调用void print()
const R b(20,52);
b.print(); //调用void print() const
return 0;
}
#include <iostream>
using namespace std;
class A {
public:
A(int i);
void print();
private:
const int a;
static const int b; //静态常数据成员
};
const int A::b=10;
A::A(int i) : a(i) { }
void A::print() {
cout << a << ":" << b <<endl;
}
int main() {
//建立对象a和b,并以100和0作为初值,分别调用构造函数,
//通过构造函数的初始化列表给对象的常数据成员赋初值
A a1(100), a2(0);
a1.print();
a2.print();
return 0;
}
常引用
- 如果在声明引用时用const修饰,被声明的引用就是常引用。
- 常引用所引用的对象不能被更新。
- 如果用常引用做形参,便不会意外地发生对实参的更改。常引用的声明形式如下:
- const 类型说明符 &引用名;
#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point类定义
public: //外部接口
Point(int x = 0, int y = 0)
: x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(const Point &p1,const Point &p2);
private: //私有数据成员
int x, y;
};
float dist(const Point &p1, const Point &p2) {
double x = p1.x - p2.x;
double y = p1.y - p2.y;
return static_cast<float>(sqrt(x*x+y*y));
}
int main() { //主函数
const Point myp1(1, 1), myp2(4, 5);
cout << "The distance is: ";
cout << dist(myp1, myp2) << endl;
return 0;
}
第六章 数组、指针与字符串
注意:
- 如果不作任何初始化,内部auto型数组中会存在垃圾数据,static数组中的数据默认初始化为0;
- 如果只对部分元素初始化,剩下的未显式初始化的元素,将自动被初始化为零;
指针的概念、定义和指针运算
内存空间的访问方式
- 通过变量名访问
- 通过地址访问
指针的概念
- 指针:内存地址,用于间接访问内存单元
- 指针变量:用于存放地址的变量
指针变量的定义
- 例:
static int i;
static int* ptr = &i;

- 例:
*ptr = 3;

与地址相关的运算——“*”和“&”
- 指针运算符
- 地址运算符:&
指针变量的初始化
- 语法形式
存储类型 数据类型 *指针名=初始地址;
- 例:int *pa = &a;
- 注意事项
- 用变量地址作为初值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致。
- 可以用一个已有合法值的指针去初始化另一个指针变量。
- 不要用一个内部非静态变量去初始化 static 指针。
指针变量的赋值运算
- 语法形式
指针名=地址
注意:“地址”中存放的数据类型与指针类型必须相符
- 向指针变量赋的值必须是地址常量或变量,不能是普通整数,例如:
- 通过地址运算“&”求得已定义的变量和对象的起始地址
- 动态内存分配成功时返回的地址
- 例外:整数0可以赋给指针,表示空指针。
- 允许定义或声明指向 void 类型的指针。该指针可以被赋予任何类型对象的地址。
例: void *general;
指针空值nullptr
- 以往用0或者NULL去表达空指针的问题:
- C/C++的NULL宏是个被有很多潜在BUG的宏。因为有的库把其定义成整数0,有的定义成 (void*)0。在C的时代还好。但是在C++的时代,这就会引发很多问题。
- C++11使用nullptr关键字,是表达更准确,类型安全的空指针
指向常量的指针
- 不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象。
- 例
int a;
const int *p1 = &a; //p1是指向常量的指针
int b;
p1 = &b; //正确,p1本身的值可以改变
*p1 = 1; //编译时出错,不能通过p1改变所指的对象
指针类型的常量
- 若声明指针常量,则指针本身的值不能被改变。
- 例
int a;
int * const p2 = &a;
p2 = &b; //错误,p2是指针常量,值不能改变
动态内存分配
动态申请内存操作符 new
- new 类型名T(初始化参数列表)
- 功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值。
- 结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。
释放内存操作符delete
- delete 指针p
- 功能:释放指针p所指向的内存。p必须是new操作的返回值。
分配和释放动态数组
- 分配:new 类型名T [ 数组长度 ]
- 数组长度可以是任何表达式,在运行时计算
- 释放:delete[] 数组名p
- 释放指针p所指向的数组。
p必须是用new分配得到的数组首地址。
- 释放指针p所指向的数组。
浅层复制与深层复制
- 浅层复制
- 实现对象间数据元素的一一对应复制。
- 深层复制
- 当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制。
更多内容,请学习课程…
