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?;蛟谥跛阉鳎壕幋a規范)
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
數組名就是數組的首地址的別名?,F在可以說:數組名就是一個指針常
量。
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. 但不能將基類對象加長,無中生有變出派生類對象