上節介紹C++的函式介紹:c++:-1,本節學習類與物件
類與物件
定義
類定義
class 類名稱
{
public:
公有成員(外部介面)
private:
私有成員
protected:
保護型成員
};
類內初始值
- 可以為資料成員提供一個類內初始值
- 在建立物件時,類內初始值用於初始化資料成員
- 沒有初始值的成員將被預設初始化。
類內初始值舉例:
class Clock {
public:
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour = 0, minute = 0, second = 0;
};
類成員的訪問控制
- 公有型別成員
在關鍵字public後面宣告,它們是類與外部的介面,任何外部函式都可以訪問公有型別資料和函式。 - 私有型別成員
在關鍵字private後面宣告,只允許本類中的函式訪問,而類外部的任何函式都不能訪問。
如果緊跟在類名稱的後面宣告私有成員,則關鍵字private可以省略。 - 保護型別成員
與private類似,其差別表現在繼承與派生時對派生類的影響不同。
物件定義
- 類名 物件名;
例:Clock myClock;
類成員的訪問許可權
- 類中成員互相訪問
直接使用成員名訪問 - 類外訪問
使用“物件名.成員名”方式訪問 public 屬性的成員
類的成員函式
- 在類中說明函式原型;
- 可以在類外給出函式體實現,並在函式名前使用類名加以限定;
- 也可以直接在類中給出函式體,形成內聯成員函式;
- 允許宣告過載函式和帶預設引數值的函式。
內聯成員函式
- 為了提高執行時的效率,對於較簡單的函式可以宣告為內聯形式。
- 行內函數體中不要有複雜結構(如迴圈語句和switch語句)。
- 在類中宣告內聯成員函式的方式:
- 將函式體放在類的宣告中。
- 使用inline關鍵字。
建構函式
實現
//第一種
Clock::Clock(int newH,int newM,int newS): hour(newH),minute(newM), second(newS) {
}
//第二種
Clock::Clock(int newH,int newM,int newS){
hour=newH;
minute=newM;
second=newS;
}
呼叫
#include "iostream"
using namespace std;
class Clock {
public:
Clock(int newH, int newM, int newS); //建構函式
Clock(); //預設建構函式
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
Clock::Clock(): hour(0),minute(0),second(0) { }//預設建構函式
//其它函式實現同前
int main() {
Clock c1(0, 0, 0); //呼叫有引數的建構函式
Clock c2; //呼叫無引數的建構函式
....
}
委託建構函式
作用:合理利用程式碼,減少程式碼重複。通過使用其他建構函式執行初始化過程
#include "iostream"
using namespace std;
class Clock {
public:
Clock(int newH, int newM, int newS); //建構函式
Clock(); //預設建構函式
void setTime(int newH, int newM, int newS);
void showTime();
private:
int hour, minute, second;
};
Clock::Clock(): hour(newH),minute(newM),second(newS) { }
Clock::Clock(0,0,0){}//預設建構函式
int main() {
Clock c1(0, 0, 0); //呼叫有引數的建構函式
Clock c2; //呼叫無引數的建構函式
....
}
賦值建構函式
(1)複製建構函式定義
複製建構函式是一種特殊的建構函式,其形參為本類的物件引用。作用是用一個已存在的物件去初始化同型別的新物件。
class類名 {
public :
類名(形參);//建構函式
類名(const 類名 &物件名);//複製建構函式
// ...
};
類名::類(const 類名&物件名)//複製建構函式的實現
{ 函式體 }
(2)隱含的複製建構函式
如果程式設計師沒有為類宣告拷貝初始化建構函式,則編譯器自己生成一個隱含的複製建構函式。
這個建構函式執行的功能是:用作為初始值的物件的每個資料成員的值,初始化將要建立的物件的對應資料成員。
(3)如果不希望物件被複制構造
- C++98做法:將複製建構函式宣告為private,並且不提供函式的實現。
- C++11做法:用“=delete”指示編譯器不生成預設複製建構函式。
例:
class Point { //Point 類的定義
public:
Point(int xx=0, int yy=0) { x = xx; y = yy; } //建構函式,內聯
Point(const Point& p) =delete; //指示編譯器不生成預設複製建構函式
private:
int x, y; //私有資料
};
(4)複製建構函式被呼叫的三種情況
- 定義一個物件時,以本類另一個物件作為初始值,發生複製構造;
- 如果函式的形參是類的物件,呼叫函式時,將使用實參物件初始化形參物件,發生複製構造;
- 如果函式的返回值是類的物件,函式執行完成返回主調函式時,將使用return語句中的物件初始化一個臨時無名物件,傳遞給主調函式,此時發生複製構造。
類的組合
即類中的成員是另一個類的物件。
類組合的建構函式
(1)原則:不僅要負責對本類中的基本型別成員資料初始化,也要對物件成員初始化。
(2)宣告形式:
類名::類名(物件成員所需的形參,本類成員形參):物件1(引數),物件2(引數),......
{
//函式體其他語句
}
初始化次序
- 首先對建構函式初始化列表中列出的成員(包括基本型別成員和物件成員)進行初始化,初始化次序是成員在類體中定義的次序。
- 成員物件建構函式呼叫順序:按物件成員的宣告順序,先宣告者先構造。
- 初始化列表中未出現的成員物件,呼叫用預設建構函式(即無形參的)初始化,處理完初始化列表之後,再執行建構函式的函式體。
前向引用宣告
類應該先宣告,後使用;如果需要在某個類的宣告之前,引用該類,則應進行前向引用宣告;前向引用宣告只為程式引入一個識別符號,但具體宣告在其他地方。
例:
class B; //前向引用宣告
class A {
public:
void f(B b); //宣告函式,形參定義時不分配記憶體空間
};
class B {
public:
void g(A a);
};
- 使用前向引用宣告雖然可以解決一些問題,但它並不是萬能的。
- 在提供一個完整的類宣告之前,不能宣告該類的物件,也不能在內聯成員函式中使用該類的物件。
- 當使用前向引用宣告時,只能使用被宣告的符號,而不能涉及類的任何細節。
例:
class Fred; //前向引用宣告
class Barney {
Fred x; //錯誤,這裡是用類定義一個物件,得分配記憶體。
};
class Fred {
Barney y;
};
UML
UML中最基礎的三個部分:
事物:
關係:聯絡事物
圖:多個有聯絡的事物
類圖
物件圖
依賴關係
繼承(泛化)關係
關聯關係
包含關係
舉例:
- 組成聚集:窗體和部件
- 共享聚集:課題組和老師
舉例
結構體
(1)結構體是一種特殊形態的類
- 與類的唯一區別:類的預設(預設)訪問許可權是private,結構體的預設訪問許可權是public
- 結構體存在的主要原因:與C語言保持相容,但C++中結構體可以有函式成員
(2)什麼時候用結構體而不用類?
- 定義主要用來儲存資料、而沒有什麼操作的型別
- 人們習慣將結構體的資料成員設為公有,因此這時用結構體更方便
定義
struct 結構體名稱 {
公有成員
protected:
保護型成員
private:
私有成員
};
初始化
如果一個結構體的全部資料成員都是公共成員,並且沒有使用者定義的建構函式,沒有基類和虛擬函式,這個結構體的變數可以用下面的語法形式賦初值
型別名 變數名 = { 成員資料1初值, 成員資料2初值, …… };
聯合體
宣告
union 聯合體名稱 {
公有成員
protected:
保護型成員
private:
私有成員
};
特點
- 成員共用同一組記憶體單元
- 任何兩個成員不會同時有效
無名聯合體
union {
int i;
float f;
}
在程式中可以這樣使用:
i = 10;
f = 2.2;// 10被覆蓋掉了
列舉類
定義
enum class 列舉型別名: 底層型別 {列舉值列表};
優勢
(1)強作用域,其作用域限制在列舉類中。
例:使用Type的列舉值General:Type::General
(2)轉換限制,列舉類物件不可以與整型隱式地互相轉換。
(3)可以指定底層型別,預設為int
例:enum class Type: char { General, Light, Medium, Heavy};
舉例
#include<iostream>
using namespace std;
enum class Side{ Right, Left };
enum class Thing{ Wrong, Right }; //不衝突
int main()
{
Side s = Side::Right;
Thing w = Thing::Wrong;
cout << (s == w) << endl; //編譯錯誤,無法直接比較不同列舉類
return 0;
}
資料共享和保護
作用域
函式原型作用域
區域性作用域
類作用域
(1)類的成員具有類作用域
其範圍包括類體和成員函式體
(2)在類作用域以外訪問類成員
- 靜態成員
通過類名,該類的物件,物件引用訪問 - 非靜態成員
通過類名,該類的物件,物件引用,物件指標訪問
檔案作用域
作用域開始於宣告,結束於檔案尾
可見行
- 若某個識別符號在外層宣告,且在內層中,如果沒有統一識別符號宣告,則可以在內層使用。
- 對於兩個巢狀的作用域,若在內層和外層都宣告瞭,則外層標識在內層不可見。
物件生存期
(1)靜態生存期
- 這種生存期與程式的執行期相同。
- 在檔案作用域中宣告的物件具有這種生存期。
- 在函式內部宣告靜態生存期物件,要冠以關鍵字static 。
(2)動態生存期
- 塊作用域中宣告的,沒有用static修飾的物件是動態生存期的物件(習慣稱區域性生存期物件)。
- 開始於程式執行到宣告點時,結束於命名該識別符號的作用域結束處。
類的靜態成員
靜態資料成員
- 用關鍵字static宣告
- 為該類的所有物件共享,靜態資料成員具有靜態生存期。
- 必須在類外定義和初始化,用(::)來指明所屬的類。
靜態函式成員
- 類外程式碼可以使用類名和作用域操作符來呼叫靜態成員函式。
- 靜態成員函式主要用於處理該類的靜態資料成員,使用類可以直接呼叫靜態成員函式。
- 如果訪問非靜態成員,要通過物件來訪問。
友元
- 友元是C++提供的一種破壞資料封裝和資料隱藏的機制。
- 通過將一個模組宣告為另一個模組的友元,一個模組能夠引用到另一個模組中本是被隱藏的資訊。
- 可以使用友元函式和友元類。
- 為了確保資料的完整性,及資料封裝與隱藏的原則,建議儘量不使用或少使用友元。
- 友關係是單向的:如果宣告B類是A類的友元,B類的成員函式就可以訪問A類的私有和保護資料,但A類的成員函式卻不能訪問B類的私有、保護資料。
友元函式
- 友元函式是在類宣告中由關鍵字friend修飾說明的非成員函式,在它的函式體中能夠通過物件名訪問 private 和 protected成員
- 作用:增加靈活性,使程式設計師可以在封裝和快速性方面做合理選擇。
- 訪問物件中的成員必須通過物件名。
友元類
- 若一個類為另一個類的友元,則此類的所有成員都能訪問對方類的私有成員。
- 宣告語法:將友元類名在另一個類中使用friend修飾說明。
舉例:
class A {
friend class B;//B是A的友元類
public:
void display() {
cout << x << endl;
}
private:
int x;
};
class B {
public:
void set(int i);
void display();
private:
A a;
};
void B::set(int i) {
a.x=i;
}
void B::display() {
a.display();
};
常型別
- 對於既需要共享、又需要防止改變的資料應該宣告為常型別(用const進行修飾)。
- 對於不改變物件狀態的成員函式應該宣告為常函式。
常物件
必須進行初始化,不能被更新。
- const 類名 物件名
例:
class A
{
public:
A(int i,int j) {x=i; y=j;}
...
private:
int x,y;
};
A const a(3,4); //a是常物件,不能被更新
常成員
用const進行修飾的類成員:常資料成員和常函式成員
(1)常成員函式
- 使用const關鍵字說明的函式。
- 常成員函式不更新物件的資料成員。
- 常成員函式說明格式:
型別說明符 函式名(參數列)const;
這裡,const是函式型別的一個組成部分,因此在實現部分也要帶const關鍵字。 - const關鍵字可以被用於參與對過載函式的區分,可以看作是過載函式。
- 通過常物件只能呼叫它的常成員函式。
(2)常資料成員
使用const說明的資料成員。
常引用
- 如果在宣告引用時用const修飾,被宣告的引用就是常引用。
- 常引用所引用的物件不能被更新。
- 如果用常引用做形參,便不會意外地發生對實參的更改。
- 常引用的宣告形式如下:
const 型別說明符 &引用名;
常陣列
陣列元素不能被更新
型別說明符 const 陣列名[大小]...
常指標
指向常量的指標
多檔案結構
一個工程可以劃分為多個原始檔:
- 類宣告檔案(.h檔案)
- 類實現檔案(.cpp檔案)
- 類的使用檔案(main()所在的.cpp檔案)
利用工程來組合各個檔案。
例如:
//檔案1,類的定義,Point.h
class Point { //類的定義
public: //外部介面
Point(int x = 0, int y = 0) : x(x), y(y) { }
Point(const Point &p);
~Point() { count--; }
int getX() const { return x; }
int getY() const { return y; }
static void showCount(); //靜態函式成員
private: //私有資料成員
int x, y;
static int count; //靜態資料成員
};
//檔案2,類的實現,Point.cpp
#include "Point.h"
#include <iostream>
using namespace std;
int Point::count = 0; //使用類名初始化靜態資料成員
Point::Point(const Point &p) : x(p.x), y(p.y) {
count++;
}
void Point::showCount() {
cout << " Object count = " << count << endl;
}
//檔案3,主函式,5_10.cpp
#include "Point.h"
#include <iostream>
using namespace std;
int main() {
Point a(4, 5); //定義物件a,其建構函式使count增1
cout <<"Point A: "<<a.getX()<<", "<<a.getY();
Point::showCount(); //輸出物件個數
Point b(a); //定義物件b,其建構函式回使count增1
cout <<"Point B: "<<b.getX()<<", "<<b.getY();
Point::showCount(); //輸出物件個數
return 0;
}
外部變數
- 如果一個變數除了在定義它的原始檔中可以使用外,還能被其它檔案使用,那麼就稱這個變數是外部變數。
- 檔案作用域中定義的變數,預設情況下都是外部變數,但在其它檔案中如果需要使用這一變數,需要用extern關鍵字加以宣告。
外部函式
- 在所有類之外宣告的函式(也就是非成員函式),都是具有檔案作用域的。
- 這樣的函式都可以在不同的編譯單元中被呼叫,只要在呼叫之前進行引用性宣告(即宣告函式原型)即可。也可以在宣告函式原型或定義函式時用extern修飾,其效果與不加修飾的預設狀態是一樣的。
將變數和函式限制在編譯單元內
使用匿名的名稱空間
:在匿名名稱空間中定義的變數和函式,都不會暴露給其它的編譯單元。
namespace { //匿名的名稱空間
int n;
void f() {
n++;
}
}
這裡被“namespace { …… }”括起的區域都屬於匿名的名稱空間。
預編譯
標準C++庫
標準C++類庫是一個極為靈活並可擴充套件的可重用軟體模組的集合。標準C++類與元件在邏輯上分為6種型別:
- 輸入/輸出類
- 容器類與抽象資料型別
- 儲存管理類
- 演算法
- 錯誤處理
- 執行環境支援
include
#include 包含指令
將一個原始檔嵌入到當前原始檔中該點處。
#include<檔名>
按標準方式搜尋,檔案位於C++系統目錄的include子目錄下
#include"檔名"
首先在當前目錄中搜尋,若沒有,再按標準方式搜尋。
define
#define 巨集定義指令
定義符號常量,很多情況下已被const定義語句取代。
定義帶引數巨集,已被行內函數取代。
#undef
刪除由#define定義的巨集,使之不再起作用。
條件編譯指令
(1)#if 和 #endif
#if 常量表示式
//當“ 常量表示式”非零時編譯
程式正文
#endif
(2)#else
#if 常量表示式
程式正文1 //當“ 常量表示式”非零時編譯
#else
程式正文2 //當“ 常量表示式”為零時編譯
#endif
(3)#elif
#if 常量表示式1
程式正文1 //當“ 常量表示式1”非零時編譯
#elif 常量表示式2
程式正文2 //當“ 常量表示式2”非零時編譯
#else
程式正文3 //其他情況下編譯
#endif
(4)#ifdef 識別符號
#ifdef 識別符號
程式段1
#else
程式段2
#endif
如果“識別符號”經#defined定義過,且未經undef刪除,則編譯程式段1;否則編譯程式段2。
(5)#ifndef 識別符號
#ifndef 識別符號
程式段1
#else
程式段2
#endif
如果“識別符號”未被定義過,則編譯程式段1;否則編譯程式段2。
程式
類的組合程式
求線段的長度
#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point類定義
public:
Point(int xx = 0, int yy = 0) {
x = xx;
y = yy;
}
Point(Point &p);
int getX() { return x; }
int getY() { return y; }
private:
int x, y;
};
Point::Point(Point &p) { //複製建構函式的實現
x = p.x;
y = p.y;
cout << "Calling the copy constructor of Point" << endl;
}
//類的組合
class Line { //Line類的定義
public: //外部介面
Line(Point xp1, Point xp2);
Line(Line &l);
double getLen() { return len; }
private: //私有資料成員
Point p1, p2; //Point類的物件p1,p2
double len;
};
//組合類的建構函式
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2) {
cout << "Calling constructor of Line" << endl;
double x = static_cast<double>(p1.getX() - p2.getX());
double y = static_cast<double>(p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
Line::Line (Line &l): p1(l.p1), p2(l.p2) {//組合類的複製建構函式
cout << "Calling the copy constructor of Line" << endl;
len = l.len;
}
//主函式
int main() {
Point myp1(1, 1), myp2(4, 5); //建立Point類的物件
Line line(myp1, myp2); //建立Line類的物件
Line line2(line); //利用複製建構函式建立一個新物件
cout << "The length of the line is: ";
cout << line.getLen() << endl;
cout << "The length of the line2 is: ";
cout << line2.getLen() << endl;
return 0;
}
結構體
#include "iostream"
using namespace std;
struct Student { //學生資訊結構體
int num; //學號
string name; //姓名,字串物件,將在第6章詳細介紹
char sex; //性別
int age; //年齡
};
int main() {
Student stu = { 97001, "Lin Lin", 'F', 19 };
cout << "Num: " << stu.num << endl;
cout << "Name: " << stu.name << endl;
cout << "Sex: " << stu.sex << endl;
cout << "Age: " << stu.age << endl;
return 0;
}
聯合體
使用聯合體儲存成績資訊,並且輸出。
#include <iostream>
using namespace std;
class ExamInfo {
private:
string name; //課程名稱
enum { GRADE, PASS, PERCENTAGE } mode;//計分方式
union {
char grade; //等級制的成績
bool pass; //只記是否通過課程的成績
int percent; //百分制的成績
};
public:
//三種建構函式,分別用等級、是否通過和百分初始化
ExamInfo(string name, char grade)
: name(name), mode(GRADE), grade(grade) { }
ExamInfo(string name, bool pass)
: name(name), mode(PASS), pass(pass) { }
ExamInfo(string name, int percent)
: name(name), mode(PERCENTAGE), percent(percent) { }
void show();
};
void ExamInfo::show() {
cout << name << ": ";
switch (mode) {
case GRADE: cout << grade; break;
case PASS: cout << (pass ? "PASS" : "FAIL"); break;
case PERCENTAGE: cout << percent; break;
}
cout << endl;
}
int main() {
ExamInfo course1("English", 'B');
ExamInfo course2("Calculus", true);
ExamInfo course3("C++ Programming", 85);
course1.show();
course2.show();
course3.show();
return 0;
}
模擬電腦執行
#include <iostream>
using namespace std;
enum CPU_Rank {P1=1,P2,P3,P4,P5,P6,P7};
class CPU
{
private:
CPU_Rank rank;
int frequency;
float voltage;
public:
CPU (CPU_Rank r, int f, float v)
{
rank = r;
frequency = f;
voltage = v;
cout << "構造了一個CPU!" << endl;
}
//複製(拷貝)建構函式
CPU(CPU &c)
{
rank= c.rank;
frequency = c.frequency;
voltage = c.voltage;
cout << "拷貝構造了一個CPU!" << endl;
}
~CPU () { cout << "析構了一個CPU!" << endl; }
CPU_Rank GetRank() const { return rank; }
int GetFrequency() const { return frequency; }
float GetVoltage() const { return voltage; }
void SetRank(CPU_Rank r) { rank = r; }
void SetFrequency(int f) { frequency = f; }
void SetVoltage(float v) { voltage = v; }
void Run() {cout << "CPU開始執行!" << endl; }
void Stop() {cout << "CPU停止執行!" << endl; }
};
enum RAM_Type {DDR2=2,DDR3,DDR4};
class RAM
{
private:
enum RAM_Type type;
unsigned int frequency;//MHz
unsigned int size;//GB
public:
RAM (RAM_Type t, unsigned int f, unsigned int s)
{
type = t;
frequency = f;
size = s;
cout << "構造了一個RAM!" << endl;
}
//複製(拷貝)建構函式
RAM(RAM &c)
{
type= c.type;
frequency = c.frequency;
size = c.size;
cout << "拷貝構造了一個RAM!" << endl;
}
~RAM () { cout << "析構了一個RAM!" << endl; }
RAM_Type GetType() const { return type; }
unsigned int GetFrequency() const { return frequency; }
unsigned int GetSize() const { return size; }
void SetType(RAM_Type t) { type = t; }
void SetFrequency(unsigned int f) { frequency = f; }
void SetSize(unsigned int s) { size = s; }
void Run() {cout << "RAM開始執行!" << endl; }
void Stop() {cout << "RAM停止執行!" << endl; }
};
enum CDROM_Interface {SATA,USB};
enum CDROM_Install_type {external,built_in};
class CD_ROM
{
private:
enum CDROM_Interface interface_type;
unsigned int cache_size;//MB
CDROM_Install_type install_type;
public:
CD_ROM (CDROM_Interface i, unsigned int s, CDROM_Install_type it)
{
interface_type = i;
cache_size = s;
install_type = it;
cout << "構造了一個CD_ROM!" << endl;
}
~CD_ROM () { cout << "析構了一個CD_ROM!" << endl; }
//複製(拷貝)建構函式
CD_ROM(CD_ROM &c)
{
interface_type= c.interface_type;
cache_size = c.cache_size;
install_type = c.install_type;
cout << "拷貝構造了一個CD_ROM!" << endl;
}
CDROM_Interface GetInterfaceType() const { return interface_type; }
unsigned int GetSize() const { return cache_size; }
CDROM_Install_type GetInstallType() const { return install_type; }
void SetInterfaceType(CDROM_Interface i) { interface_type = i; }
void SetSize(unsigned int s) { cache_size = s; }
void SetInstallType(CDROM_Install_type it) { install_type = it; }
void Run() {cout << "CD_ROM開始執行!" << endl; }
void Stop() {cout << "CD_ROM停止執行!" << endl; }
};
class COMPUTER
{
private:
CPU my_cpu;
RAM my_ram;
CD_ROM my_cdrom;
unsigned int storage_size;//GB
unsigned int bandwidth;//MB
public:
COMPUTER (CPU c, RAM r, CD_ROM cd,unsigned int s,unsigned int b);
~COMPUTER () { cout << "析構了一個COMPUTER!" << endl; }
void Run()
{
my_cpu.Run();
my_ram.Run();
my_cdrom.Run();
cout << "COMPUTER開始執行!" << endl;
}
void Stop()
{
my_cpu.Stop();
my_ram.Stop();
my_cdrom.Stop();
cout << "COMPUTER停止執行!" << endl;
}
};
COMPUTER::COMPUTER(CPU c, RAM r, CD_ROM cd,unsigned int s,unsigned int b):
my_cpu(c),my_ram(r),my_cdrom(cd)
{
storage_size = s;
bandwidth = b;
cout << "構造了一個COMPUTER!" << endl;
}
int main()
{
CPU a(P6,300,2.8);
a.Run();
a.Stop();
cout<<"*************************\n";
RAM b(DDR3,1600,8);
b.Run();
b.Stop();
cout<<"*************************\n";
CD_ROM c(SATA,2,built_in);
c.Run();
c.Stop();
cout<<"*************************\n";
COMPUTER my_computer(a,b,c,128,10);
cout<<"*************************\n";
my_computer.Run();
my_computer.Stop();
cout<<"*************************\n";
return 0;
}
輸出:
構造了一個CPU!
CPU開始執行!
CPU停止執行!
*************************
構造了一個RAM!
RAM開始執行!
RAM停止執行!
*************************
構造了一個CD_ROM!
CD_ROM開始執行!
CD_ROM停止執行!
*************************
拷貝構造了一個CPU!
拷貝構造了一個RAM!
拷貝構造了一個CD_ROM!
拷貝構造了一個CPU!
拷貝構造了一個RAM!
拷貝構造了一個CD_ROM!
構造了一個COMPUTER!
析構了一個CD_ROM!
析構了一個RAM!
析構了一個CPU!
*************************
CPU開始執行!
RAM開始執行!
CD_ROM開始執行!
COMPUTER開始執行!
CPU停止執行!
RAM停止執行!
CD_ROM停止執行!
COMPUTER停止執行!
*************************
析構了一個COMPUTER!
析構了一個CD_ROM!
析構了一個RAM!
析構了一個CPU!
析構了一個CD_ROM!
析構了一個RAM!
析構了一個CPU!
變數的生存期與可見性
#include<iostream>
using namespace std;
int i = 1; // i 為全域性變數,具有靜態生存期。
void other(){
static int a = 2;
static int b;
// a,b為靜態區域性變數,具有全域性壽命,區域性可見。
//只第一次進入函式時被初始化
int c = 10; // C為區域性變數,具有動態生存期,
//每次進入函式時都初始化。
a += 2; i += 32; c +=5;
cout<<"---OTHER---\n";
cout<<" i: "<<i<<" a: "<<a<<" b: "<<b<<" c: "<<c<<endl;
b = a;
}
int main() {
static int a;//靜態區域性變數,有全域性壽命,區域性可見。
int b = -10; // b, c為區域性變數,具有動態生存期。
int c = 0;
cout << "---MAIN---\n";
cout<<" i: "<<i<<" a: "<<a<<" b: "<<b<<" c: "<<c<<endl;
c += 8; other();
cout<<"---MAIN---\n";
cout<<" i: "<<i<<" a: "<<a<<" b: "<<b<<" c: "<<c<<endl;
i += 10; other();
return 0;
}
輸出:
---MAIN---
i: 1 a: 0 b: -10 c: 0
---OTHER---
i: 33 a: 4 b: 0 c: 15
---MAIN---
i: 33 a: 0 b: -10 c: 8
---OTHER---
i: 75 a: 6 b: 4 c: 15
靜態成員
靜態資料成員
#include <iostream>
using namespace std;
class Point { //Point類定義
public: //外部介面
Point(int x = 0, int y = 0) : x(x), y(y) { //建構函式
//在建構函式中對count累加,所有物件共同維護同一個count
count++;
}
Point(Point &p) { //複製建構函式
x = p.x;
y = p.y;
count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
void showCount() { //輸出靜態資料成員
cout << " Object count = " << count << endl;
}
private: //私有資料成員
int x, y;
static int count; //靜態資料成員宣告,用於記錄點的個數
};
int Point::count = 0;//靜態資料成員定義和初始化,使用類名限定
int main() { //主函式
Point a(4, 5); //定義物件a,其建構函式回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //輸出物件個數
Point b(a); //定義物件b,其建構函式回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
b.showCount(); //輸出物件個數
return 0;
}
輸出:
Point A: 4, 5 Object count = 1
Point B: 4, 5 Object count = 2
靜態函式成員
#include <iostream>
using namespace std;
class Point { //Point類定義
public: //外部介面
Point(int x = 0, int y = 0) : x(x), y(y) { //建構函式
//在建構函式中對count累加,所有物件共同維護同一個count
count++;
}
Point(Point &p) { //複製建構函式
x = p.x;
y = p.y;
count++;
}
~Point() { count--; }
int getX() { return x; }
int getY() { return y; }
static void showCount() { //輸出靜態資料成員
cout << " Object count = " << count << endl;
}
private: //私有資料成員
int x, y;
static int count; //靜態資料成員宣告,用於記錄點的個數
};
int Point::count = 0;//靜態資料成員定義和初始化,使用類名限定
int main() { //主函式
Point::showCount();
Point a(4, 5); //定義物件a,其建構函式回使count增1
cout << "Point A: " << a.getX() << ", " << a.getY();
a.showCount(); //輸出物件個數
Point b(a); //定義物件b,其建構函式回使count增1
cout << "Point B: " << b.getX() << ", " << b.getY();
b.showCount(); //輸出物件個數
return 0;
}
輸出:
Object count = 0
Point A: 4, 5 Object count = 1
Point B: 4, 5 Object count = 2
友元
友元函式
使用友元函式計算兩點間的距離
#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point類宣告
public: //外部介面
Point(int x=0, int y=0) : x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(Point &a, Point &b);//友元函式,訪問類的私有資料
private: //私有資料成員
int x, y;
};
float dist( Point& a, Point& b) {
double x = a.x - b.x;
double y = a.y - b.y;
return static_cast<float>(sqrt(x * x + y * y));
}
int main() {
Point p1(1, 1), p2(4, 5);
cout <<"The distance is: ";
cout << dist(p1, p2) << endl;
return 0;
}
輸出:
The distance is: 5
常型別
常成員函式
#include<iostream>
using namespace std;
class R {
public:
R(int r1, int r2) : r1(r1), r2(r2) { }
void print();
void print() const;
private:
int r1, r2;
};
void R::print() {
cout << r1 << ":" << r2 << endl;
}
void R::print() const {
cout << r1 << ";" << r2 << endl;
}
int main() {
R a(5,4);
a.print(); //呼叫void print()
const R b(20,52);
b.print(); //呼叫void print() const
return 0;
}
輸出:
5:4
20;52
常資料成員
#include <iostream>
using namespace std;
class A {
public:
A(int i);
void print();
private:
const int a;
static const int b; //靜態常資料成員
};
const int A::b=10;
A::A(int i) : a(i) //只能在此賦值
{ }
void A::print() {
cout << a << ":" << b <<endl;
}
int main() {
//建立物件a和b,並以100和0作為初值,分別呼叫建構函式,
//通過建構函式的初始化列表給物件的常資料成員賦初值
A a1(100), a2(0);
a1.print();
a2.print();
return 0;
}
輸出:
100:10
0:10
常引用
#include <iostream>
#include <cmath>
using namespace std;
class Point { //Point類定義
public: //外部介面
Point(int x = 0, int y = 0): x(x), y(y) { }
int getX() { return x; }
int getY() { return y; }
friend float dist(const Point &p1,const Point &p2);
private: //私有資料成員
int x, y;
};
//const Point &p1, const Point &p2:常引用,不改變引用物件值
float dist(const Point &p1, const Point &p2) {
double x = p1.x - p2.x;
double y = p1.y - p2.y;
return static_cast<float>(sqrt(x*x+y*y));
}
int main() { //主函
const Point myp1(1, 1), myp2(4, 5);
cout << "The distance is: ";
cout << dist(myp1, myp2) << endl;
return 0;
}
輸出:
The distance is: 5
客戶機類
/* client.h*/
//防止重複包含標頭檔案
#ifndef CLIENT_H_
#define CLIENT_H_
class Client {
static char ServerName;
static int ClientNum;
public:
static void ChangeServerName(char name);
static int getClientNum();
};
#endif //CLIENT_H_
/* client.cpp*/
#include "client.h"
void Client::ChangeServerName(char name) {
Client::ServerName = name;
Client::ClientNum ++;
}
int Client::getClientNum() {
return Client::ClientNum;
}
/* test.h*/
#include <iostream>
#include "client.h"
using namespace std;
int Client::ClientNum = 0;
char Client::ServerName = 'a';
int main()
{
Client c1;
c1.ChangeServerName('a');
cout << c1.getClientNum() << endl;
Client c2;
c2.ChangeServerName('b');
cout << c2.getClientNum() << endl;
return 0;
}
習題
(1)抽象使得協同工作的開發人員可以更多地關注他人的程式碼功能而非程式碼實現。
對
(2)在C++中,編譯系統自動為一個類生成預設建構函式的條件是
- 該類沒有定義任何有參建構函式
- 該類沒有定義任何無參建構函式
- 該類沒有定義任何建構函式(對)
- 該類沒有定義任何成員函式
(3)以下關於結構體和聯合體說法正確的是
- 結構體的預設訪問許可權是public(對)
- 在c++中,結構體可以有函式成員(對)
- 聯合體各成員共用同一組記憶體單元(對)
- 聯合體中任何兩個成員可以同時有效
(4)靜態函式不可直接訪問物件的變數
對
(5)以下關於外部變數和外部函式的說法,錯誤的是
- 外部變數的宣告可以是引用性的宣告
- 靜態變數和靜態函式即使使用extern宣告,它們的使用範圍仍然被限定在定義檔案中
- 外部變數可以為多個原始檔所共享
- 外部函式和外部變數在宣告時,都不能省略關鍵詞extern(錯)
分析:
使用外部變數時,需要使用extern;使用外部函式時,要麼使用extern,要麼重新宣告函式原型
(6)以下關於預處理的說法,錯誤的是
- 預處理在編譯前進行
- 預處理指令需要分號結尾(錯)
- 每條預處理指令必須單獨佔用一行
- 預處理指令可以出現在程式的任何位置
分析:預處理指令無需結尾分號