Introduction to C++ (Season 1)
Unit 1: Overview of C++
第1单元: 不识庐山真面目 – C++概览
Section 1 : About This Course
第1节:关于C++课程
"C++" and its father
- C++ == C Plus Plus == CPP
- Father of C++: Bjarne Stroustrup
Year | C++ Standard | Informal name |
---|---|---|
2014 | ISO/IEC 14882:2014 | C++14 |
2011 | ISO/IEC 14882:2011 | C++11 |
2007 | ISO/IEC TR 19768:2007 | C++TR1 |
2003 | ISO/IEC 14882:2003 | C++03 |
1998 | ISO/IEC 14882:1998 | C++98 |
Section 2 : Prelude : Aloha World!
第2节:你好,世界!
Program Files (代码文件/程序文件)
- Header file
- 头文件
- Source file
- 源文件
Coding Conventions (编码规范)
Why Coding Conventions?
- 让程序具有好的可读性
- “避免日后有人(或者自己)指着你的代码骂娘:这特么谁写的破烂玩意”(来自:知乎-Gui Wulei。或在知乎搜索:编码规范)
Google开源项目风格指南(中文版)
- https://github.com/yangyubo/zh-google-styleguide
- 其中C++规范5万多字,过于庞大
geosoft.no的编码风格指南(英文版)
- http://geosoft.no/development/cppstyle.html
- 4.9版本有94条规则。规则相对简明
Section 1 : Primitive Data Types & Operations
第1节:基本数据类型与运算
Unit 3: More than C
第3单元:更上一层楼 – 超越C的语法
Review: typedef and #define
In C99
//C99: no "bool"
typedef int BOOL; //分号结尾
#define TRUE 1 //无分号
#define FALSE 0
typedef Blah Blah Blah NewTypeName
#define MACRONAME Blah-Blah-Blah
typedef unsigned int UInt;
typedef unsigned int* PInt;
3. Names representing types must be in mixed case starting with upper case.
3. 代表类型的名字必须首字母大写并且其它字母大小写混合
例如:Line, SavingsAccount
#define A_NAME "alpha"
#define YOU 2
5. Named constants (including enumeration values) must be all uppercase
using underscore to separate words.
5. 命名常量(包括枚举值)必须全部大写并由下划线分隔单词
例如:MAX_ITERATIONS, COLOR_RED, PI
Data type : bool (布尔类型)
In C++: bool, true, false
bool isMyBook;
bool isRunning = false;
bool isBoy( );
bool hasLicense();
bool canWork();
bool shouldSort();
bool and int
0 -> false
true -> 1
non-zero -> true
Example
'a' -> ?
26. The prefix is should be used for boolean variables and methods.
26. 布尔变量/函数的命名应使用前缀“is”
例如:isSet, isVisible, isFinished, isFound, isOpen
Example of bool (bool示例)
#include <iostream>
int main() {
bool isAlpha;
isAlpha = false;
if (!isAlpha) {
std::cout << "isAlpha=" << isAlpha << std::endl;
std::cout << std::boolalpha <<
"isAlpha=" << isAlpha << std::endl;
}
return 0;
}
/*
39. The incompleteness of split lines must be made obvious.
39. 断行必须很明显。
在逗号或运算符后换行,新行要对齐
The output
isAlpha=0
isAlpha=false
*/
Type conversion (类型转换)
C++ Style: static_cast<type> value
cout << static_cast<double>(1) / 2;
cout << 1 / 2;
//45. Type conversions must always be done explicitly. Never rely on implicit type conversion.
//45. 类型转换必须显式声明。永远不要依赖隐式类型转换
//例如:floatValue = static_cast<float>(intValue); // NOT: floatValue = intValue;
Declaring and Initializing Variables (变量初始化)
int x = 1; //C & C++
int x(1); //C++, OO style
int x; x(1); //Error!
Section 2 : Function
第2节:函数
Scope of Variable
变量作用域
Scope of Local Variables 1 (局部变量的作用域)
Variables | Position (位置) | Scope (作用域) |
---|---|---|
in C89 | must be defined in the front of function body(必须在函数体前面定义) | starts from it's definition to the end of the function(从定义位置到函数尾) |
in C++ | can be defined anywhere inside the functionbody (可在函数体内任意位置定义) | starts from it's definition to the end of the block (e.g.function body/for body/…) (从定义位置到块尾) |
Unary Scope Resolution (一元作用域解析运算符)
If a local variable name is the same as a global variable name, you can accessthe global variable using ::globalVariable. (局部变量名与全局变量名相同时,可使用 :: 访问全局变量)
The :: operator is known as the unary scope resolution.
#include <iostream>
using namespace std;
int v1 = 10;
int main() {
int v1 = 5;
cout << "local variable v1 is " << v1 << endl;
cout << "global variable v1 is " << ::v1 << endl;
return 0;
}
Overloading Functions
重载函数
max() : works only with the int datatype (只用于int)
int max(int num1, int num2)
{
if (num1 > num2)
return num1;
else
return num2;
}
But what about floating-point numbers? (如果遇到浮点数怎么办)
The solution is to create another function with the same name but different parameters. (方法是生成一个同名不同参数的函数)
编译器如何匹配重载函数调用?
-> 看参数:
- 个数
- 类型
- 顺序
Ambiguous Invocation (二义调用)
Ambiguous Invocation: There may be two or more possible matches for an
invocation of a function, but the compiler cannot determine the most specific
match. (某函数调用有多个匹配项,但编译器无法确定正确的项)
Ambiguous invocation is a compilation error. (会导致编译错误)
#include <iostream>
using namespace std;
int maxNumber(int num1, double num2)
{
if (num1 > num2)
return num1;
else
return num2;
}
double maxNumber(double num1, int num2)
{
if (num1 > num2)
return num1;
else
return num2;
}
int main()
{
cout << maxNumber(1, 2) << endl;
return 0;
}
Default Argument
默认参数值
C++ allows you to declare functions with default argument values.
(可以声明带默认参数值的函数)
The default values are passed to the parameters when a function is invoked without the arguments.
(无参调用函数时,默认值会被传递给形式参数)
参数列表中默认值参数应后置
void t3 (int x, int y=0, int z=0);
void t4 (int x=0, int y=0, int z=0);
t3 (1); // y,z: default value
t4 (1, 2); // z: default value
(C++03/C++11) A default argument shall not be redefined by a later
declaration (not even to the same value).
void printArea(double radius = 1);
int main(){
printArea();
printArea(4);
return 0;
}
void printArea(double radius) {
// Do something
}
Inline Functions
内联函数
Using functions in a program:
Advantages(优点): 易读易维护
Drawbacks (缺点): 运行时性能开销
• 函数调用时:参数及部分CPU寄存器的
寄存器内容进栈,控制流跳转
• 函数返回时:返回值及寄存器值出栈,
控制流跳转
Inline functions
目的:减小性能开销
方法:代码插入到调用处
结果:导致程序变大
How to declare an inline function
inline int max (int a, int b) {
return (a > b ? a : b);
}
But NOT:
int max (int a, int b);
// Call max()
inline int max (int a, int b) {
return (a > b ? a : b);
}
Restrictions for inline function (内联函数的使用限制)
Desire for short functions (适用于短函数)
NOT suitable for long functions thatare called in multiple places (不适于多处调用的长函数)
内联函数只是一种编译机制
“inline”是对编译器的请求,而不是命令。
函数调用的开销
对小的函数不可忽略
对重量级的函数是可以忽略的
大多数编译器并不把带有循环、递归等或者代码比较多的函数进行内联编译。
Section 3 : Reference & Dynamic Memory
第3节:引用与动态内存管理
Reference
引用
Reference (引用)
A reference is an alias for another variable. (引用就是另一个变量的别名)
Any changes made through the reference variable are actually performed on the original variable(通过引用所做的读写操作实际上
是作用于原变量上).
To declare a reference variable:
int x;
int& rx = x;
or
int x, &rx = x;
51. C++ pointers and references should have their
reference symbol next to the type rather than to the name.
51. C++指针与引用符号应靠近其类型而非名字。
例如: float* x; // NOT: float *x;
int& y; // NOT: int &y;
Function Parameters: Pass By Reference (函数参数:引用传递)
You can use a reference variable as a parameter in a function and pass a
regular variable to invoke the function. (引用可做函数参数,但调用时只需
传普通变量即可)
When you change the value through the reference variable, the original value
is actually changed. (在被调函数中改变引用变量的值,则改变的是实参的
值)
Comparision: 3 swap() functions
//pass by value
void swap(int x, int y){
int t;
t=x; x=y; y=t;
}
int main() {
int a(5), b(10);
cout << "Before: a=" << a <<
" b=" << b << endl;
swap( a, b );
cout << "After: a=" << a <<
"b=" << b << endl;
return 0;
}
Before: a=5 b=10
After: a=5 b=10
//pointer as formal params
void swap(int* x, int* y){
int t;
t=*x; *x=*y; *y=t;
}
int main() {
int a(5), b(10);
cout<< "Before: a=" << a <<
" b=" << b << endl;
swap( &a, &b );
cout<< "After: a=" << a <<
<<"b="<<b<<endl;
return 0;
}
Before: a=5 b=10
After: a=10 b=5
//reference as formal params
void swap(int& x, int& y){
int t;
t=x; x=y; y=t;
}
int main() {
int a(5), b(10);
cout<< "Before: a=" << a <<
" b=" << b << endl;
swap( a, b );
cout << "After: a=" << a <<
"b="<<b<<endl;
return 0;
}
Before: a=5 b=10
After: a=10 b=5
Dynamic Memory
动态内存管理
**Dynamic memory: Allocate/Release **
C++中通过运算符new申请动态内存
new <类型名> (初值) ; //申请一个变量的空间
new <类型名>[常量表达式] ; //申请数组
如果申请成功,返回指定类型内存的地址;
如果申请失败,返回空指针(整数0)。
动态内存使用完毕后,要用delete运算符来释放。
delete <指针名>; //删除一个变量/对象
delete []<指针名>; //删除数组空间
70. "0" should be used instead of "NULL".
70. 用“0”代替“NULL” is part of the standard C library, but is made obsolete in C++.。
因为“NULL”是C语言标准库的内容,但是在C++中已经废止了。
Dynamic memory: Examples
11 | C | C++ |
---|---|---|
AllocateRelease | malloc();free(); | new delete |
Example 1 | char* s = (char*)malloc(1);free(s); | char* s = new char(97);delete s; |
Example 2 | int* p = (int) malloc(410);free(p); | int* p = new int[10];delete [] p; |
Example 3 | int** q = (int) malloc(4103);free(q); | int** q = new int[10][3];int (*q)[3] = new int[10][3];(暂不做要求)delete [] p; |
new
delete
char* s = new char(97);
delete s;
int* p = new int[10];
delete [] p;
int** q = new int[10][3];\\int** q = new int[10][3];error C2440: “初始化”: 无法从“int (*)[3]”转换为“int **”、int (*q)[3] = new int[10][3];(暂不做要求)
delete [] p;
Section 4 : Simplified Memory Model for C/C++
第4节:C/C++的简化内存模型
Simplified Memory Model (C++的内存模型)
- Stack (栈)
编译器自动分配释放 - Heap (堆)
一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 - Global/Static (全局区/静态区)
全局变量和静态变量的存储是放在一块的。
可以简单认为:
• 程序启动全局/静态变量就在此处
• 程序结束释放 - Constant (常量区)
可以简单理解为所有常量都放在一起
该区域内容不可修改
Example of memory allocation (C++程序的内存示例)
堆向高地址方向生长
栈向低地址方向生长
常量区 全局/静态区 堆 栈
0x1000-------------0xFFFF
Location of a variable (变量存放位置)
int arr[3];//全局/静态区
int myFunc()
{
int a;//栈
char *p;//栈
char* str=“hello world”//str->栈,“hello world”->常量区
}
int myFunc1(int* pi)//栈
{
char *pc;//栈
pc= static_cast<char*> new char[8];//堆
}
普通变量内存模型
普通变量
int a, b=0; a=b;
a和b都是变量的名,对a和b的访问实际上访问的是a和b这两个变量中存
储的值
a和b的地址分别是 &a 和 &b
数组内存模型
对于数组a[],a是数组a[]的首地址的别名
要访问每个数组元素的值,使用a[0], a[1],…
要访问一个地址所存的内容,使用 “*”
访问数组中第一个元素可以使用 *(a+0)
访问数组中第二个元素 *(a+1)
a[]
a[0] a[1] a[2] a[3]
Section 5 : Const
第5节:常量
const datatype CONSTANTNAME = VALUE;
const double PI = 3.14159;
const int SIZE = 3;
int const X = 5;
const in C vs in C++
in C (C89), const means "ReadOnly Variable" (只读变量)
in C++, const means "Constant" (常量)
const int ARRAY_SIZE = 10;
int arr[ARRAY_SIZE]; //OK in C++, Error in C
5. Named constants (including enumeration values) must be all
uppercase using underscore to separate words.
5. 符号常量(包括枚举值)必须全部大写并用下划线分隔单词
例如:MAX_ITERATIONS, COLOR_RED, PI
const and pointers (常量与指针)
two features of a pointer(指针的两个属性):
pointer variable (指针变量本身)
data that the pointer points to (指针变量所指向的数据)
const + pointer
指针 | 被指数据 |
---|---|
variable | variable |
variable | constant |
constant | variable |
constant | constant |
A variable pointer to a constant value
Shortly: Pointer to Constant (常量指针/常指针)
特征:指针所指向的内容不可以通过指针的间接引用(*p)来改变。
const int* p1;
const int x = 1;
p1 = &x; //指针 p1的类型是 (const int*)
*p1 = 10; // Error!
An invariable pointer to a variable value
Pointer Constant (指针常量)
指针本身的内容是个常量,不可以改变。
int x = 1, y = 1;
int* const p2 = &x; //常量 p2的类型是 (int*)
*p2 = 10; // Okay! -> x=10
p2 = &y; // Error! p2 is a constant
数组名就是数组的首地址的别名。现在可以说:数组名就是一个指针常
量。
Memory location of pointers (指针的内存布局)
指针布局实例: (以下代码在某函数内部)
const int a = 5;
int b = 9;
const int* pa = &a; //pointer to constant
int* const pb = &b; //pointer constant
Summary (总结)
在前先读,在前不变
* (指针)和 const(常量) 谁在前先读谁 ;
* 代表被指的数据,名字代表指针地址
const在谁前面谁就不允许改变。
关于指针和数组的关系,是C语言课程中非常重要的一个知识。
在这里仅给出一些结论性的内容。如果你对该知识点还比较模糊,那么,思考清楚下面的结论,对你掌握指针与数组有很大帮助。
我们定义:
int iarray[5],*ip;
然后
ip=&iarray[0];
那么有如下结论:(<==>表示在一般情况下可以互换使用)
ip+k <==> &iarray[k] <==> iarray+k <==> value of ip + k*sizeof(int)
以及
*(ip+k) <==> iarray[k] <==> ip[k] <==> *(iarray+k)
Section 1 : Concepts of Class
第1节:类的概念
Unit 4: Objects and Classes
第4单元: 物以类聚 –对象和类
Features of OO (面向对象的特征)
Abstraction (抽象)
Polymorphism (多态)
Inheritance (继承)
Encapsulation (封装)
What does an object consist of? (对象由什么构成)
An object has a unique identity, state, and behaviors.(对象具有唯一的标识、状态和行为)
The state of an object consists of a set of data fields (also known as properties) with
their current values. (对象状态由数据域(也称为“属性”)及其当前值构成)
The behavior of an object is defined by a set of functions. (对象的行为由一组函数定义)
Classes
A class uses variables(变量) to define data fields(定义数据域) and
functions(函数) to define behaviors(定义行为).
Additionally, a class provides a special type of functions, known as
constructors, which are invoked to construct objects from the class. (类中有
一种特殊的“构造函数”,在创建对象时被自动调用)
C++ Data Types (C++数据类型分类)
C++ Data Types
structured
array struct union class
address
pointer reference
floating
float double long double
integral
char short int long bool
Section 2 : Create Objects and Access the members
第2节:创建对象并访问对象成员
Constructors:
Initialize objects (构造函数:初始化对象)
Has the same name as the defining class (与类同名)
NO return value (including "void"); (无返回值)
constructors can be overloaded (可重载)
may be no arguments (可不带参数)
A class may be declared without constructors (类可不声明构造函数)
1. A no-arg constructor with an empty body is implicitly declared in the class.
(编译器会提供一个带有空函数体的无参构造函数)
2. This constructor, called a default constructor is provided automatically only if no constructors are explicitly declared in the class.
(只有当未明确声明构造函数时,编译器才会提供这个构造函数,并称之为“默认构造函数”)
class Circle {
public:
double radius;
Circle() { }
};
Constructing Objects (创建对象)
Without Arguments: (无参数)
ClassName objectName;
For example:
Circle circle1; // the no-arg constructor
// is invoked
With Arguments: (带参数)
ClassName objectName(arguments);
For Example:
Circle circle2(5.5);
class Circle {
public:
double radius;
Circle() {
radius = 1;
}
Circle(double newRadius) {
radius = newRadius;
}
//……
};
**Object Member Access Operator(对象访问运算符) **
To access the data & functions of an object: (访问对象中的数据和函数)
the dot operator (.), namely, the object member access operator.
objectName.dataField // 访问对象的数据域
objectName.function(arguments) // 调用对象的一个函数
A Simple Circle Class
#include <iostream>
using namespace std;
class Circle {
public:
// The radius of this circle
double radius;
// Construct a circle object
Circle() {
radius = 1;
}
// Construct a circle object
Circle(double newRadius) {
radius = newRadius;
}
// Return the area of this circle
double getArea() {
return radius * radius * 3.14159;
}
};
int main() {
Circle circle1;
Circle circle2(5.0);
cout << "The area of the circle of radius " <<
circle1.radius << " is " << circle1.getArea() << endl;
cout << "The area of the circle of radius " <<
circle2.radius << " is " << circle2.getArea() << endl;
// Modify circle radius
circle2.radius = 100.0;
cout << "The area of the circle of radius " <<
circle2.radius << " is " << circle2.getArea() << endl;
return 0;
}
Section 3 : More on Creating Objects
第3节:关于创建对象的更多细节
Naming Objects and Classes (为对象和类命名)
When you declare a custom class, capitalize the first letter of each word in aclass name; (声明一个自定义的类时,类名中的单词要首字母大写)
for example, the class names Circle, Rectangle, and Desk.
The class names in the C++ library are named in lowercase. (C++标准库中的类名是小写的)
The objects are named like variables. (对象的命名方式与变量类似)
Class is a Type (类是一种数据类型)
use primitive data types to define variables. (用基本数据类型定义变量)
use class names to declare objects. In this sense, a class is also a data type. (用类名定义对象)
3. Names representing types must be in mixed case starting with upper case.
3. 代表类型的名字必须首字母大写并且其它字母大小写混合
例如:Line, SavingsAccount
Memberwise Copy (成员拷贝)
How to copy the contents from one object to the other?(如何将一个对象的内容拷贝给另外一个对象)
use the assignment operator(使用赋值运算符): =
By default, each data field of one object is copied to its counterpart in the other object.(默认情况下,对象中的每个数据域都被拷贝到另一对象的对应部分)
Example: circle2 = circle1;
copies the radius in circle1 to circle2.
After the copy, circle1 and circle2 are still two different objects, but with the sameradius.
Anonymous Object (匿名对象)
Occasionally, you may create an object and use it only once. (有时需要创建一个只用一次的对象)
In this case, you don’t have to name the object. (此时,无需给对象命名)
Such objects are called anonymous objects. (这种对象叫做匿名对象)
The syntax is
ClassName(); //using the no-arg
ClassName(arguements); //using the constructor with arguments
int main() {
Circle circle1, circle2;
circle1 = Circle();
circle2 = Circle(5);
cout << "Area is " << Circle().getArea() << endl;
cout << "Area is " << Circle(5).getArea() << endl;
return 0;
}
Section 4 : Separating Declaration from Implementation
第4节:将声明与实现分离
Separating Declaration from Implementation (声明与实现分离)
C++ allows you to separate class declaration from implementation. (C++中,类声明与实现可以分离)
The class declaration describes the contract of the class (类声明描述了类的约定)
The class implementation implements the contract. (类实现则实现该约定)
.h: 类声明
.cpp: 类实现
FunctionType ClassName :: FunctionName (Arguments) { //… }
Inline Declaration & Inline Function (内联声明与内联函数)
When a function is implemented inside a class declaration, it automatically
becomes an inline function. (当函数在类声明中实现,它自动成为内联函数)
class A {
public:
A() {
// do something;
}
double f1() {
// return a number
}
double f2();
};
double A::f2() {
//do something
}
class A {
public:
A() {
// do something;
}
double f1() {
// return a number
}
double f2();
};
inline double A::f2() {
//do something
}
Section 5 : Object Pointer & Dynamic Object
第5节:对象指针与动态对象
Accessing Object Members via Pointers (用指针访问对象成员)
Object names cannot be changed once they are declared. (对象名声明后无法修改)
However, object pointers can be assigned new object names(对象指针可以指向新的对象名)
1. Circle circle1;
2. Circle *pCircle = &circle1;
3. cout << "The radius is " << (*pCircle).radius << endl;
4. cout << "The area is " << (*pCircle).getArea() << endl;
5. (*pCircle).radius = 5.5;
6. cout << "The radius is " << pCircle->radius << endl;
7. cout << "The area is " << pCircle->getArea() << endl;
Creating Dynamic Objects on Heap (在堆中创建对象)
Object declared in a function is created in the stack.(在函数中声明的对象都在栈上创建); When the function returns, the object is destroyed (函数返回,则对象被销毁).
To retain the object, you may create it dynamically on the heap using the newoperator. (为保留对象,你可以用new运算符在堆上创建它)
ClassName *pObject = new ClassName(); //用无参构造函数创建对象
Circle *pCircle1 = new Circle(); //用无参构造函数创建对象
Circle *pCircle2 = new Circle(5.9); //用有参构造函数创建对象
ClassName *pObject = new ClassName(arguments); //用有参构造函数创建对象
//程序结束时,动态对象会被销毁,或者
delete pObject; //用delete显式销毁
Section 6 : The C++ string Class
第6节:C++字符串类
The C++ string Class
C++ 使用 string 类处理
字符串
string类中的函数
- 构造
- 追加
- 赋值
- 位置与清除
- 长度与容量
- 比较
- 子串
- 搜索
- 运算符
注意事项
操作string对象中的字符串内容时,有时会用到“index”。
"Welcome to C and C++!"
0号位置0号字符
从7号位置开始的5个字符“ to C”
第1个字符'W'
第8个字符' ' (空格)
很多string的函数接受两个数字参数: index, n
index: 从index号位置开始
n: 之后的n个字符
Constructing a String (创建 string 对象)
Create an empty string using string’s no-arg constructor(用无参构造函数创建一个空字串):
string newString;
Create a string object from a string value or from an array of characters (由一个字符串常量或字符串数组创建string对象) :
string message("Aloha World!");
char charArray[] = {'H', 'e', 'l', 'l', 'o', '\0'};
string message1(charArray);
Appending a String (追加字符串)
You can use several overloaded functions to add new contents to a string. (一系列的重载函数可以将新内容附加到一个字符串中)
string s1("Welcome");
s1.append(" to C++"); // appends " to C++" to s1
cout << s1 << endl; // s1 now becomes Welcome to C++
string s2("Welcome");
s2.append(" to C and C++", 3, 2); // appends " C" to s2
cout << s2 << endl; // s2 now becomes Welcome C
string s3("Welcome");
s3.append(" to C and C++", 5); // appends " to C" to s3
cout << s3 << endl; // s3 now becomes Welcome to C
string s4("Welcome");
s4.append(4, 'G'); // appends "GGGG" to s4
cout << s4 << endl; // s4 now becomes WelcomeGGGG
Assigning a String (为字符串赋值)
You can use several overloaded functions to assign new contents to a string(一系列的重载函数可以将一个字符串赋以新内容)
string s1("Welcome");
s1.assign("Dallas"); // assigns "Dallas" to s1
cout << s1 << endl; // s1 now becomes Dallas
string s2("Welcome");
s2.assign("Dallas, Texas", 1, 3); // assigns "all" to s2
cout << s2 << endl; // s2 now becomes all
string s3("Welcome");
s3.assign("Dallas, Texas", 6); // assigns "Dallas" to s3
cout << s3 << endl; // s3 now becomes Dallas
string s4("Welcome");
s4.assign(4, 'G'); // assigns "GGGG" to s4
cout << s4 << endl; // s4 now becomes GGGG
Functions at, clear, erase, and empty
at(index): 返回当前字符串中index位置的字符
clear(): 清空字符串
erase(index, n): 删除字符串从index开始的n个字符
empty(): 检测字符串是否为空
string s1("Welcome");
cout << s1.at(3) << endl; // s1.at(3) returns c
cout << s1.erase(2, 3) << endl; // s1 is now Weme
s1.clear(); // s1 is now empty
cout << s1.empty() << endl; // s1.empty returns 1 (means true)
Comparing Strings (比较字符串)
compare() 函数用于比较两个字符串。它与C语言中的 strcmp() 函数很像。
string s1("Welcome");
string s2("Welcomg");
cout << s1.compare(s2) << endl; // returns -2
cout << s2.compare(s1) << endl; // returns 2
cout << s1.compare("Welcome") << endl; // returns 0
Obtaining Substrings (获取子串)
at() 函数用于获取一个单独的字符;而substr() 函数则可以获取一个子串
string s1("Welcome");
cout << s1.substr(0, 1) << endl; // returns W; 从0号位置开始的1个字符
cout << s1.substr(3) << endl; // returns come; 从3号位置直到末尾的子串
cout << s1.substr(3, 3) << endl; // returns com;从3号位置开始的3个字符
Searching in a String (搜索字符串)
find() 函数可以在一个字符串中搜索一个子串或者一个字符
string s1("Welcome to HTML");
cout << s1.find("co") << endl; // returns 3; 返回子串出现的第一个位置
cout << s1.find("co", 6) << endl; // returns -1 从6号位置开始查找子串出现的第一个位置
cout << s1.find('o') << endl; // returns 4 返回字符出现的第一个位置
cout << s1.find('o', 6) << endl; // returns 9 从6号位置开始查找字符出现的第一个位置
Inserting and Replacing Strings (插入和替换字符串)
insert() : 将某个字符/字符串插入到当前字符串的某个位置
replace() 将本字串从某个位置开始的一些字符替换为其它内容
string s1("Welcome to HTML");
s1.insert(11, "C++ and ");
cout << s1 << endl; // s1 becomes Welcome to C++ and HTML
string s2("AA");
s2.insert(1, 4, 'B'); //在1号位置处连续插入4个相同字符
cout << s2 << endl; // s2 becomes to ABBBBA
string s3("Welcome to HTML");
s3.replace(11, 4, "C++"); //从11号位置开始向后的4个字符替换掉。注意'\0'
cout << s3 << endl; // returns Welcome to C++
**String Operators (字符串运算符) **
string s1 = "ABC"; // The = operator
string s2 = s1; // The = operator
for (int i = s2.size() - 1; i >= 0; i--)
cout << s2[i]; // The [] operator
string s3 = s1 + "DEFG"; // The + operator
cout << s3 << endl; // s3 becomes ABCDEFG
s1 += "ABC";
cout << s1 << endl; // s1 becomes ABCABC
s1 = "ABC";
s2 = "ABE";
cout << (s1 == s2) << endl; // Displays 0
cout << (s1 != s2) << endl; // Displays 1
cout << (s1 > s2) << endl; // Displays 0
cout << (s1 >= s2) << endl; // Displays 0
cout << (s1 < s2) << endl; // Displays 1
cout << (s1 <= s2) << endl; // Displays 1
Operator | Description |
---|---|
[ ] | 用数组下标运算符访问字符串中的字符 |
= | 将一个字符串的内容复制到另一个字符串 |
+ | 连接两个字符串得到一个新串 |
+= | 将一个字符串追加到另一个字符串末尾 |
<< | 将一个字符串插入一个流 |
>> | 从一个流提取一个字符串,分界符为空格或者空结束符 |
==, !=, <,<=, >, >= | 用于字符串比较 |
Section 7 : Data Field Encapsulation
第7节:数据域封装
数据域采用public的形式有2个问题
First, data may be tampered. (数据会被类外
的方法篡改)
Second, it makes the class difficult to
maintain and vulnerable to bugs. (使得类难
于维护,易出现bug)
Accessor and Mutator (访问器与更改器)
To read/write private data, we need get/set function(为读写私有数据,需要get/set函数)
get function is referred to as a getter (获取器,or accessor),
set function is referred to as a setter (设置器,or mutator).
Signature of get function (General form) (get函数的一般原型)
returnType getPropertyName()
Signature of get function (Bool type) (布尔型get函数的原型)
bool isPropertyName()
Signature of set function (set函数的原型)
void setPropertyName(dataType propertyValue)
26. 布尔变量/函数的命名应使用前缀“is”
There are a few alternatives to the is prefix
that fit better in some situations. These are the
has, can and should prefixes:
"is"前缀有时会有更好的替换,包括has, can和should
例如:bool hasLicense(); bool canEvaluate();
bool shouldSort();
Section 8 : The Scope of Variables & "this" pointer
第8节:变量作用域与this指针
The Scope of Variables – Review (变量作用域-回顾)
C语言的“函数”章节,介绍了3种变量作用域
Global variables (全局变量)
are declared outside all functions and are accessible to all functions in its scope. (在所有函数外面声明并在其作用域内可被所有函数访问)
The scope starts from its declaration and continues to the end of the program. (作用域起于声明,止于程序结束)
Local variables (局部变量)
are defined inside functions. (在函数内定义)
The scope starts from its declaration and continues to the end of the block thatcontains the variable. (作用域起于声明,止于包含该变量的块尾)
Static local variables (静态局部变量)
permanently stored in the program.
can be used in the next call of the function
The Scope of Data Fields in Class (类中数据域的作用域)
The data fields
are declared as variables inside class (被定义为变量形式)
are accessible to all constructors and functions in the class.(可被类内所有函数访问)
Data fields and functions can be declared in any order in a class. (数据域与函数可按任意顺序声明)
Hidden by same name (同名屏蔽)
If a local variable has the same name as a data field: (若成员函数中的局部变量与某数据域同名)
the local variable takes precedence (局部变量优先级高)
the data field with the same name is hidden. (同名数据域在函数中被屏蔽)
The this Pointer
How do you reference a class’s hidden data field in a function? (如何在函数内访问类中被屏蔽的数据域)
this keyword
a special built-in pointer (特殊的内建指针)
references to the calling object. (引用当前函数的调
用对象)
class Circle {
public:
Circle();
Circle(double radius)
{
this->radius = radius;
}
private:
double radius;
public:
void setRadius(double);
//……
};
Screen类中有两个函数move() 和 set(),如何设计这两个函数的返回值,才能使得下面语句是合法的?
myScreen.move(4,0).set('#');
Simple way to avoid name hidden (避免重名屏蔽的简单方法)
class Circle {
public:
Circle();
Circle(double radius)
{
//this->radius = radius;
radius_ = radius;
}
private:
double radius_;
public:
void setRadius(double);
//……
};
11. Private class variables should have underscore suffix.
11. 私有类成员变量名应有下划线后缀
例:
class SomeClass {
private:
int length_;
}
代码中容易区分类成员变量及函数局部变量
也有些规范中使用下划线前缀。但使用后缀让名字可
读性更好
Section 9 : Passing Objects to Functions
第9节:对象作为函数参数
Passing Objects to Functions (对象作为函数参数)
You can pass objects by value or by reference. (对象作为函数参数,可以按值传递也可以按引用传递)
Section 10 : Array of Objects
第10节:对象数组
Array of Objects (对象数组)
声明方式1
Circle circleArray[10];
Question:
circleArray[1].getRadius() 的值是
多少?
声明方式2
Circle circleArray[3] = {
Circle(3),
Circle(4),
Circle(5)
};
Section 11 : Class Abstration and Encapsulation
第11节:类抽象与封装
Class Abstraction and Encapsulation (类抽象与封装)
Class abstraction (类抽象)
to separate class implementation from the use of the class. (将类的实现与使用分离开)
The creator provides a class description (类创建者提供类的描述)
The user of the class does not need to know how the class is implemented. (使用者不需知道类是如何实现的)
Class encapsulation (类封装)
The detail of implementation is encapsulated and hidden from the user.(类实现的细节被封装起来,并对用户是隐藏的)
Section 12 : Constructor Initializer Lists
第12节:构造函数初始化列表
Constructor Initializer (构造函数初始化)
在构造函数中用初始化列表初始化数据域
ClassName (parameterList)
: dataField1(value1), dataField2(value2)
{
// Something to do
}
Why we need a Constructor Initializer Lists? (为何需要初始化列表)
If a data field is an object type (Object in Object) (若类的数据域是一个对象类型)
The Role of Default Constructor (默认构造函数的角色)
If a data field is an object type (Object in Object) (若类的数据域是一个对象
类型)
the default constructor is automatically invoked to construct an object for the data field.(该对象的无参构造函数会被自动调用)
If a default constructor does not exist, a compilation error will be reported. (若没有无参构造函数,则编译器报错)
You can use the Constructor Initializer to construct the object manually (你也可以在初始化列表中手工构造对象)
Unit05 - More on Objects and Classes
第5单元: 万类霜天竞自由 – 对象和类的更多内容
Section 01 : Immutable Objects and Classes; Preventing Multiple Declaration
第01节:不可变对象、不可变类;避免多次声明
Immutable Objects and Classes
immutable object(不可变对象): The contents of an object cannot be changed(except through memberwise copy) once the object is created.(对象创建后,其内容不可改变,除非通过成员拷贝)
immutable class (不可变类) : The class of immutable object (不可变对象所
属的类)
Example
A class is NOT necessarily immutable even: (下述情况中,类也不一定是“不可变类”)
with all private data fields (所有数据域均为私有属性)
no mutators. (无更改器函数)
int main(){
Person person(111223333, 1970, 5, 3);
Date *pDate = person.getBirthDate();
pDate -> setYear(2010);
cout << "birth year after the change is " <<
person.getBirthDate() -> getYear() << endl;
return 0;
}
4. Variable names must be in mixed case starting with lower case.
4. 变量名必须混合大小写且以小写字母开头
例如:line, savingsAccount
How to make a class immutable? (让类成为“不可变类”)
Mark all data fields private (所有数据域
均设置为“私有”属性)
No mutator functions (没有更改器函数)
No accessor that would return a
reference/pointer to a mutable data field
object
(也没有能够返回可变数据域对象的引用
或指针的访问器)
Preventing Multiple Declarations (避免多次声明)
- Put "#pragma once" in the first line of .h file (使用“杂注”)
依赖于编译器
古老的编译器不支持 - Use #ifndef preprocessing instructions in .h file (使用“宏”)
#ifndef FILENAME_H
#define FILENAME_H
// The contents of the header file
#endif FILENAME_H
Section 02 : Instance and Static Members
第02节:实例成员与静态成员
Rules for Static member function (使用静态成员函数的规则)
Rules1: How to invoke Static member function: (调用静态成员函数)
Rules2: Static member functions access other members: (静态成员函数访问其他成员)
主调函数\被访问 | 静态 | 非静态 |
---|---|---|
静态 | 通过类名/对象名 | 只能通过对象名 |
非静态 | 只能通过对象名 | |
Static member: example
class A {
public:
A(int a=0){x=a;}
static void f1();
static void f2(A a);
private:
int x;
static int y;
};
void A::f2(A a)
{
cout<< y;
cout<< x; //Error
cout<< a.x; //OK通过对象访问非静态数据成员
}
void A::f1()
{
cout<<y<<endl;//可直接访问静态数据成员
}
void main ()
{
A::f1();//通过类名调用静态成员函数
A mA(3);
A::f2(mA);//通过类名调用静态成员函数
mA.f1();//通过对象调用静态成员函数
}
Use Class Name (for readablity) (使用类名访问静态变量/函数)
Use ClassName::functionName(arguments) to invoke a static function and ClassName::staticVariable.
class A {
public:
A(int a=0){x=a;}
static void f1();
static void f2(A
a);
private:
int x;
static int y;
};
void A::f2(A a){
cout<< A::y;
cout<< a.x;
}
void A::f1() {
cout<<A::y<<endl;
}
void main
(){
A::f1();
A mA(3);
A::f2(mA);
mA.A::f1();
}
Instance or Static? (实例还是静态)
When to use STATIC in class? (何时在类中使用静态成员)
A variable or function that is not dependent on a specific instance of the class should be a static variable or function. (变量和函数不依赖于类的实例时)
For example
every circle has its own radius. Radius is dependent on a specific circle. Therefore, radius is an instance variable of the Circle class. Since the getArea function is dependent on a specific circle, it is an instance function.
Since numberOfObjects is not dependent on any specific instance, it should be declared static.
Section 03 : Destructor and Friend
第03节:析构函数与友元
Destructors (析构函数)
Destructors are the opposite of constructors. (dtor vs ctor)
-- | Destructor | Constructor |
---|---|---|
When to invoke(何时调用) | when the object is destroyed | when an object is created |
Prototype(原型) | C::~C( ) | C::C(arguments) |
Default prototype(默认函数的原型) | C::~C( ) | C::C( ) |
What if no explicit decl? (没有显式声明怎么办) | Compiler will create a default one (编译器会生成默认函数) | |
Overloadable(可否重载) | No, only 1 | Yes |
Why Friend (为何需要友元)
Private members: CANNOT be
accessed from outside of the class.
(私有成员无法从类外访问)
Circle c1();
c1.radius = 1;
Occasionally, it is convenient to
allow some trusted functions and
classes to access a class’s private
members. (但有时又需要授权某些
可信的函数和类访问这些私有成
员)
Friend functions and classes (友元函数和友元类)
C++ enables you to use the friend
keyword to declare friend functions
and friend classes for a class (用
friend关键字声明友元函数或者友
元类)
Disadvantage of "friend": break the
encapsulation
Section 04 : Copy Constructor
第04节:拷贝构造函数
Copy Constructors
Every class has a copy constructor.
ClassName (ClassName&); 类的对象的引用
Circle (Circle&);
Circle (const Circle&);
Default copy ctor
simply copies each data field in one
object to its counterpart in the other
object. (默认copy ctor简单地将参数对
象中的每个数据域复制到新建对象中)
class X { //C++03/11: 12.8
// ...
public:
X(int);
X(const X&, int = 1);
};
X a(1); // calls X(int);
X b(a, 0); // calls X(const X&, int);
X c = b; // calls X(const X&, int);
Shallow Copy vs. Deep Copy (浅拷贝和深拷贝)
Shallow copy: if the field is a pointer to some object, the address of the
pointer is copied rather than its contents. (拷指针,而非指针指向的内容)
Deep copy: Copy the contents that pointed by the pointer (拷指针指向的内容)
Shallow Copy 1 (浅拷贝1)
Before person2 is copied to person1, the birthDate field of person1 and
person2 point to two different Date objects. (person2拷贝到person1之前,他
们的birthDate指向不同的Date对象)
Shallow Copy 2 (浅拷贝2)
After person2 is copied to person1, the birthDate field of person1 and person2
point to the same Date object. (person2拷贝给person1后,他们的birthDate都
指向同一个Date对象)
Customizing Copy Constructor(定制拷贝构造函数)
Shallow copy:
default copy constructor (默认构造函
数)
assignment operator for copying = (用
于拷贝对象的赋值运算符)
Deep copy: you should implement
the copy ctor. (自行编写拷贝构造
函数)
class X {
// ...
};
X a; // calls X();
X b(a); // calls X(const X&, int);
X c = b; // calls X(const X&, int);
a = c; // calls X& operator= (const X&);
Section 05 : Case Study
第05节:示例分析
Case study1: The Course Class
Course
-name: string//The name of the course. (课程名称)
-students: string[100]//The students who take the course.(选课学生)
-numberOfStudents: int//The number of students (default: 0).(学生数量,默认为0)
+Course(name: &string)//Creates a Course with the specified name.(用制定名字创建课程)
+getName(): string//Returns the course name. (返回课程名)
+addStudent(student: &string): void//
Adds a new student to the course list. (增加一个选课学生)
+getStudents(): string*//Returns the array of students for the course. (返回所有选课学生)
+getNumberOfStudents(): int//Returns the number of students for the course.(返回选课学生数量)
```
Case study 2: The StackOfInteger Class
A stack is a data structure that holds
objects in a last-in first-out
fashion.(栈:后进先出)
Stack applications:
函数调用时,主函数传给子函数的参
数先进栈,进入子函数后,子函数的
局部变量也按序在栈中建立
子函数返回时,局部变量出栈、参数
出栈。
Members of Stack (stack类的成员)
The StackOfIntegers class encapsulates the stack storage and provides the
operations for manipulating the stack. (stack类封装了栈的存储空间并提供
了操作栈的函数)
### Section 06 : The C++ vector Class
### 第06节:vector 类
The C++ vector Class
Limitation of using array to store values: the array size is fixed in the class
declaration. (用数组存放数据时,容量大小不可变)
The vector object can increase its size if needed.(vector对象容量可自动增大)
Examples
```
vector<int> intVector;
// Store numbers 1, ..., 10 to the vector
for (int i = 1; i < 10; i++)
intVector.push_back(i + 1);
25. Iterator variables should be called i, j, k etc.
25. 迭代变量名应该用 i, j, k 等
此外,变量名 j, k应只被用于嵌套循环
尽量多用i做循环变量;
尽量令i的作用域限制在循环块内
```
### Section 07 : More Programming Style Guidelines
### 第07节:更多编码规范
```
Two basic guidelines (两个最基础的规范)
1. Any violation to the guide is allowed if it enhances readability.
1. 只要能增强可读性,你在编码时可以不遵守这些编程风格指南
一语道尽规范的目的
2. The rules can be violated if there are strong personal objections against them.
2. 如果你有很好的个人理由的话,可以不遵守这些规范
Why do I violate the rules? (为嘛我要违反规则)
46. Variables should be initialized where they are declared.
46.变量应在其声明处初始化
int x = 1;
int x1 = 1;
int x2 = 1;
// blah blah blah
int x = 8;
请
在
函
数
开
头
定
义
变
量
71. Basic indentation should be 2.
Indentation of 1 is too small to emphasize the
logical layout of the code. Indentation larger than 4
makes deeply nested code difficult to read and
increases the chance that the lines must be split.
Choosing between indentation of 2, 3 and 4, 2 and 4
are the more common, and 2 chosen to reduce the
chance of splitting code lines.
73. The class declarations should have the following form:
class SomeClass : public BaseClass
{
public://编辑器/开发环境有关
...
protected:
...
private:
...
}
```
```
Layouts for loop statements (循环语句的布局)
76. A for statement should have the following form:
for (initialization; condition; update) {
statements;
}
77. An empty for statement should have the following form:
for (initialization; condition; update)
;
This emphasizes the fact that the for statement is empty and it
makes it obvious for the reader that this is intentional. Empty
loops should be avoided however.
78. A while statement should have the following form:
while (condition) {
statements;
}
79. A do-while statement should have the following form:
do {
statements;
} while (condition);
74. Method definitions should have the following form:
74. 方法定义应遵循如下形式
void someMethod()
{
...
}
```
About white space (关于空格)
```
84.
‐ Conventional operators should be surrounded by a space character. (运算符前后应有空格)
‐ C++ reserved words should be followed by a white space.(C++保留字后边应有空格)
‐ Commas should be followed by a white space. (逗号后面跟空格)
‐ Colons should be surrounded by white space.(冒号前后有空格)
‐ Semicolons in for statments should be followed by a space character.(for语句的分号后有空格)
例如:
a = (b + c) * d; // NOT: a=(b+c)*d
while (true) // NOT: while(true)
{
...
doSomething(a, b, c, d); // NOT: doSomething(a,b,c,d);
case 100 : // NOT: case 100:
for (i = 0; i < 10; i++) { // NOT: for(i=0;i<10;i++){
...
```
### Section 01 : Inheritance
### 第01节:继承
回顾
面向对象的4个特点:
A(抽象) P(多态)I(继承)E(封装)
前两个单元:AE
本单元: PI
Inheritance (继承)
巴巴家族的能力
自行车的例子
Example: GeometricObject (几何对象)
几何对象
矩形 圆
形
……
派生类与基类之间是一种________关系
正确答案:is-a
类B继承类A,代码可以写作:
class B: A {
//Some data field and function
};
正确答案: B.×
### Section 02 : Constructor and Destructor
### 第02节:构造函数和析构函数
派生类继承的成员
派生类继承 ctor 和 dtor 吗?
派生类不继承的特殊函数
构造函数 (C++11已允许继承)
析构函数
作为特权地位的友元函数
赋值运算符函数
```
struct A {
A(int i) {}
A(double d, int i) {}
// ...
};
struct B : A {
using A::A; // 继承基类构造函数
int d{0}; // 新的变量初始化方法
};
int main() {
B b(1); // b.d 初始化为 0
}
```
Calling Base Class Constructors (调用基类构造函数)
Ctors of base class can only be invoked from the constructors of the derived classes. (基类构造函数只能由派生类构造函数调用)
The syntax to invoke it is as follows:
```
DerivedClass(parameterList) : BaseClass() {
// Perform initialization
}
// Or
DerivedClass(parameterList) : BaseClass(argumentList) {
// Perform initialization
}
```
No-Arg Constructor in Base Class(基类的无参构造函数)
Rules for invoke constructors in derived class
A constructor in a derived class must always invoke a constructor in its base class. (派生类构造函数必须调用基类构造函数)
If a base constructor is not invoked explicitly, the base class’s no-arg constructor is invoked by default. (若基类ctor未被显式调用,基类的无参构造函数就会被调用)
Constructor and Destructor Chaining (构造和析构函数链)
constructor chaining (构造函数链)
Constructing an instance of a class
invokes all the base class along the
inheritance chain. (构造类实例会沿着
继承链调用所有的基类ctor)
Invoke sequence: base first, derive next
destructor chaining (析构函数链)
Conversely, the destructors are
automatically invoked in reverse
order(dtor与ctor正好相反)
Invoke sequence: derive first, base next
no-arg constructor (无参构造函数)
If a class is designed to be extended, provide a no-arg constructor. (若你的类想被别人扩展,那么就提供一个无参构造函数)
```
#include <string>
class Fruit {
public:
Fruit(int id) {//A no-arg ctor is expected
}
std::string s;
};
class Apple: public Fruit {
public:
Apple() {//Apple() : Fruit() {}
}
};
int main () {
Apple apple;
}
```
```
34. 文件扩展名:头文件用.h,源文件用 .cpp (c++, cc也可)
35. A class should be declared in a header file and defined in a
source file where the name of the files match the name of the class.
35. 类应该在头文件中声明并在源文件中定义,俩文件名字应
该与类名相同
例如:MyClass.h, MyClass.c++
例外的是,模板类的声明和定义都要放在头文件中
49. Class variables should never be declared public.
49. 类成员变量不可被声明为public
说明:公有变量违背了C++的信息隐藏原则。例外的是,
如果class只是一个数据结构,类似C语言中的struct,则
可将类变量声明为公有
```
1派生类中不能继承基类中的
B.析构函数
C.友元函数
D.赋值运算符函数
2
基类的构造函数能够由派生类中的任何函数调用
×
3
类A的析构函数在类外部定义时可以写为如下形式:
~A::A() {
//do something
}×
4
构造函数和析构函数是自动调用的√
### Section 03 : Redefining Functions
### 第03节:函数重定义
Redefining Functions
GeometricObject::toString()
function returns a string describe the
GeometricObject. (该函数返回一个
字符串用于描述对象)
You can redefine toString() in
Circle and Rectangle to return a
string descrie Circle or Rectangle
object. (你可以重定义派生类的该
函数以描述派生类对象)
```
string GeometricObject::toString() {
return "Geometric object color " + color +
" filled " + ((filled) ? "true" : "false");
}
string Circle::toString() {
return "Circle object color " + color +
" filled " + ((filled) ? "true" : "false");
}
string Rectangle::toString() {
return "This is a rectangle object");
}
```
Redefine (hide) (重定义/隐藏)
```
class GeometricObject{
public:
string toString () { return "parent";}
};
class Circle: public GeometricObject {
public:
string toString () { return "child";}
void g () { cout<<toString(); }
};
int main( ) {
Circle circle;
cout << circle.toString ();
circle.GeometricObject::toString ();
return 0;
}
```
Redefine v.s. Overload (重定义与重载)
Overload Functions (§5.7) (重载函数)
more than one function with the same name (多个函数名字相同)
But different in at least one of the signatures: (但至少一个特征不同)
• parameter type (参数类型)
• parameter number (参数数量)
• parameter sequence (参数顺序)
Redefine Functions (重定义函数)
The functions have the same signature (函数特征相同)
• Name (同名)
• Parameters (including type, number and sequence) (同参数:类型,数量和顺序)
• Return type (返回值类型)
Defined in base class and derived class, respectively (在基类和派生类中分别定义)
### Section 04 : Polymorphism and Virtual Functions
### 第04节:多态和虚函数
What is Polymorphism?
广义的多态:不同对象对于相同的消息有不同的响应,就是OOP中的多
态性。
截止目前:多态性有两种表现的方式
重载:
class C {
public:
int f(int x);
int f( );
};
重定义:不同的对象调用重定义
函数,表现出不同的行为
class A { int f() {return 1;} };
class B: public A
{ int f() {return 8;} };
A x; B y;
x.f();
y.f();
**Binding**
联编(Binding): 确定具有多态性的
语句调用哪个函数的过程。
Static Binding (静态联编)
在程序编译时确定调用哪个函数
例:函数重载
Dynamic Binding (动态联编)
在程序运行时,才能够确定调用哪个
函数
用动态联编实现的多态,也称为运行
时的多态。
Why Run-time Polymorphism
Example: Why we need
run-time polymorphism?
(运行时多态的必要性).
How do we implement polymorphism (如何实现多态)
virtual function (虚函数)
Override (覆盖) : redefining a
virtual function in a derived class.
(在派生类中重定义一个虚函数)
Polymorphism: using dynamic binding (动态联编)
How to enable dynamic
binding? (如何使得函数能够
实现动态联编)
The function must be declared
virtual in the base class. (基类同
名虚函数)
The variable that references the
object for the function must
contain the address of the object.
(访问对象的成员函数时,要用
指向对象的指针或者对象引用)
```
class C {
public:
virtual string toString() {
return "class C";
}
};
void displayObject(C *p){
// cout << p->toString().data() << endl;
cout << p->toString().c_str() << endl;
}
int main(){
A a = A(); B b = B(); C c = C();
displayObject(&a);
displayObject(&b);
displayObject(&c);
return 0;
}
```
Note
If a function is defined virtual in a
base class, it is automatically virtual
in all its derived classes. (基类定义
了虚同名函数,那么派生类中的
同名函数自动变为虚函数)
Virtual functions:
Virtual function table (虚函数表)
Run-time binding (运行时联编)
More overhead in run-time than nonvirtual
function (开销大)
```
class C {
public:
virtual string toString() {
return "class C";
}
};
class B: public C{
string toString() {
return "class B";
}
};
class A: public B{
string toString() {
return "class A";
}
};
```
Summary: static binding v.s. dynamic binding
基类与派生类中有同名函数
1. 通过派生类对象访问同名函数
静态联编
2. 通过基类对象的指针访问同名函数
静态联编
3. 通过基类对象的指针访问同名虚函数
动态联编
Summary: 静态联编的简单示例
```
class B { public: f(){…}};
class P: public B { public: f(){…}};
class Q: public B { public: f(){…}};
main () {
P p; Q q;
p.f();调用的是P::f()
q.f();调用的是Q::f()
}
```
```
Summary: 通过基类指针访问同名函数的简单示例
class B { public: f(){…} };
class P: public B { public: f(){…} };
class Q: public B { public: f(){…} };
main () {
B* b_ptr; P p; Q q;
b_ptr=&p;
b_ptr->f();调用的是B::f()
b_ptr=&q;
b_ptr->f();调用的是B::f()
}
```
Summary: 动态联编的简单示例-指针形式
```
class B { public: virtual f() {} };
class P: public B { public: f() {} };
class Q: public B { public: f() {} };
main () {
B* b_ptr; P p; Q q;
b_ptr=&p;
b_ptr->f();调用的是P::f()
b_ptr=&q;
b_ptr->f();调用的是Q::f()
}
```
Summary:动态联编的简单示例-引用形式
```
class B { public: virtual f() {} };
class P: public B { public: f() {} };
class Q: public B { public: f() {} };
main () {
P p; Q q;
B& b_ptr1=p;
b_ptr1.f();调用的是P::f()
B& b_ptr2=q;
b_ptr2.f();调用的是Q::f()
}
```
### Section 05 : Accessibility (Visibility)
### 第05节:访问控制 (可见性控制)
The protected Keyword
the private and public keywords
to specify whether data fields and functions can be accessed from the outside of the
class. (说明数据及函数是否可以从类外面访问)
Private members can only be accessed from the inside of the class (私有成员只能在类
内的函数访问)
Public members can be accessed from any other classes. (公有成员可被任何其他类访
问)
A protected data field or a protected function in a base class can be accessed
by name in its derived classes. (保护属性的数据或函数可被派生类成员访
问)
访问属性示例
```
#include <iostream>
using namespace std;
class B {
public:
int i;
protected:
int j;
private:
int k;
};
class A: public B {
public:
void display() {
cout << i << endl; // Fine, cannot access it
cout << j << endl; // Fine, cannot access it
cout << k << endl; // Wrong, cannot access it
}
};
int main() {
A a;
cout << a.i << endl; // Fine, cannot access it
cout << a.j << endl; // Wrong, cannot access it
cout << a.k << endl; // Wrong, cannot access it
return 0;
}
```
1. 公有继承
公有继承的派生类定义形式:
class 派生类名:public 基类名{
派生类新成员定义;
};
```
1. 基类成员 在派生类中的访问属性不变。
2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能
访问基类的私有成员;
3. 派生类以外的其它函数 可以通过派生类的对象,访问从基类继
承的公有成员, 但不能访问从基类继承的保护成员和私有成员。
```
2. 私有继承
私有继承的派生类定义形式:
class 派生类名:private 基类名{
派生类新成员定义;
};
```
1. 基类成员 在派生类中的访问属性都变成 private。
2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能
访问基类的私有成员;
3. 派生类以外的其它函数 不能通过派生类的对象,访问从基类继
承的任何成员。
```
3. 保护继承
私有继承的派生类定义形式:
class 派生类名:protected 基类名{
派生类新成员定义;
};
```
1. 基类成员 公有成员和保护成员在派生类中变成保护类型的,基类
的私有成员属性不变。
2. 派生类的成员函数 可以访问基类的公有成员和保护成员,不能
访问基类的私有成员;
3. 派生类以外的其它函数 不能通过派生类的对象,访问从基类继
承的任何成员。
```
### Section 06 : Abstract Class and Pure Virtual Function
### 第06节:抽象类与纯虚函数
Abstract Classes (抽象类)
classes become more specific &
concrete with each new derived class.
(派生类时,新类会越来越明确和具
体)
move back up to the parent and
ancestor , the classes become more
general and less specific. (沿着派生类
向父类移动,类会越来越一般化和
抽象)
Sometimes a base class is so abstract that it cannot
have any specific instances. Such a class is referred
to as an abstract class (类太抽象以至于无法实例
化就叫做抽象类)
Abstract Functions (抽象函数)
All geometric objects have: areas & perimeters
Declare getArea() and getPerimeter() in GeometricObject class? (在几何对象类中声明
计算面积和周长的函数?)
Is it meaningful for getArea() in GeometricObject ? (这种声明有实际意义
吗?)
NO, the implementation is dependent on the specific type of geometric object.
virtual double getArea() = 0;
virtual double getPerimeter() = 0;
Such functions are referred to as abstract functions.
Abstract class: the class which contains abstract functions
### Section 07 : Dynamic Cast
### 第07节:动态类型转换
Dynamic Casting – Why (为何需要动态类型转换)
```
void displayGeometricObject(GeometricObject &object)
{
cout << "The area is " << object.getArea() << endl;
cout << "The perimeter is " << object.getPerimeter() << endl;
}
```
Suppose you wish to modify this function to display radius, diameter, area, and perimeter if the object is a circle. How can this be done? (怎么才能让这个函数显示圆对象的半径、直径面积和周长)
Dynamic Casting Example
dynamic_cast operator
cast a parameter of the
GeometricObject type
into a Circle type (将基
类类型参数转换为派
生类类型)
then invoke the
getRadius() and
getDiameter() functions
defined in the Circle
class (然后调用派生类
中独有的函数)
```
// A function for displaying a geometric object
void displayGeometricObject(GeometricObject &object)
{
cout << "The area is " << object.getArea() << endl;
cout << "The perimeter is " << object.getPerimeter() << endl;
GeometricObject *p = &object;
Circle *p1 = dynamic_cast<Circle*>(p);
if (p1 != 0)
{
cout << "The radius is " << p1->getRadius() << endl;
cout << "The diameter is " << p1->getDiameter() << endl;
}
}
```
Upcasting and Downcasting (向上/向下 转型)
upcasting : Assigning a pointer of a derived class type to a pointer of its base
class type (将派生类类型指针赋值给基类类型指针)
downcasting : Assigning a pointer of a base class type to a pointer of its
derived class type. (将基类类型指针赋值给派生类类型指针)
Upcasting and Downcasting (向上/向下 转型 续)
Upcasting can be performed implicitly without using the dynamic_cast
operator. (上转可不适用dynamic_cast而隐式转换)
GeometricObject *g = new Circle(1);
Circle *c = new Circle(2);
g = c; //Correct
However, downcasting must be performed explicitly. (下转必须显式执行)
For example, to assign p to p1, you have to use
c = dynamic_cast<Circle *>(g);
typeid operator (typeid运算符)
How to obtain the information about the class of the object? (如何获取对象所属的类的信息)
typeid operator: return a reference to an object of class type_info. (typeid运算符返回一个type_info对象的引用)
to display the class name for object x. (显示对象x的类名)
string x;
cout << typeid(x).name() << endl;
基类对象和派生类对象的互操作
问题1:对象内存布局
GeometricObject G; Circle C;
GeometricObject* pG=&G;
Circle* pC=&C;
问题2:互操作
G=C; //Y
C=G; //N
pG=&C; //Y
pC=&G; //N
GeometricObject &rG=C; //Y
Circle &rC=G; //N
Warning
1. 可将派生类对象截断,只使用继承来的信息
2. 但不能将基类对象加长,无中生有变出派生类对象