一、類的靜態成員
1.目的
靜態成員(關鍵字:static)是解決同一個類的不同對象之間數據和函數共享問題。區分全局變量,全局變量也能實現數據共享,但安全性和封裝性被破壞了。
2.靜態數據成員
(1) 使用關鍵字static在類內聲明,類外定義和初始化,用::
指明所屬的類。
(2) 靜態數據成員屬於整個類,每個對象均可引用。靜態數據成員的值對所有對象來說都是一樣的。如果改變它的值,則各個對象中這個數據成員的值都同時改變。
(3) 靜態數據成員在內存中只佔一份空間,是在所有對象之外單獨開闢的空空間。也就是說爲對象分配的空間不包括靜態數據成員佔的空間。靜態數據成員在程序編譯時被分配空間,結束時釋放空間。
(4) 只要在類中定義了靜態數據成員,即使不定義對象,也要爲靜態數據成員分配空間,它可以被引用。
(5) 靜態數據成員可以通過對象名引用,也可以過類名引用。
//1.通過類名訪問
Student :: m_tatal = 10;
//2.通過對象名來訪問
Student stu("小明", 15, 88);
stu.m_total = 20;//注意他們受訪問權限的控制
//3.通過對象指針來訪問
Student *pstu = new Student("小芳", 19, 96);
pstu->m_total = 20;//靜態變量可以訪問,可以修改
//以上三種方式等效
(6) 如果未對其賦初值,自動賦初值爲0.
int Student::m_total = 0; 等價於 int Student::m_total;
3.靜態成員函數
(1) 類外代碼可以使用類名和作用域操作符來調用靜態成員函數。公有的靜態成員函數可以通過類名或對象名調用,而一般的非靜態成員函數只能通過對象名調用。
(2) 靜態成員函數可以直接訪問該類的靜態數據和靜態函數。作用不是爲了對象間的溝通,而是爲了能處理靜態數據成員。
(3) 靜態成員函數不屬於某一對象,爲各個對象共有,並且可以通過類來直接調用,所有沒有this指針,無法對一個對象中的非靜態成員進行默認訪問。但可以通過參數傳遞方式得到對象名,通過對象名訪問。
例1:
#include <iostream>
using namespace std;
class Point {
public:
Point(int xx=0, int yy=0) {X=xx; Y=yy; countP++; } //每創建一個對象,對靜態數據成員有影響,所以更新coutP
Point(Point &p){X=p.X;Y=p.Y; countP++; } //所有對象共同維護同一個countP
~Point(){countP--; } //每調用一次釋放一個對象,與之相關的靜態數據成員也要進行更新
int GetX() {return X;}
int GetY() {return Y;}
static void ShowCount(){cout<<", Object id="<<countP<<endl;} //靜態成員函數
private:
int X,Y;
static int countP; //類內靜態數據成員聲明
};
int Point::countP=0; //類外靜態數據成員定義和初始化,使用類名限制
void main() {
Point A(4,5);
cout<<"Point A:"<<A.GetX()<<","<<A.GetY();
A.ShowCount(); //用對象名調用用靜態成員函數
Point B(A);
cout<<"Point B:"<<B.GetX()<<","<<B.GetY();
Point.ShowCount(); //用類名調用靜態成員函數
}
上述例子注意是靜態數據成員的值與對象相關時,在構造函數裏要進行相應的操作更新。靜態成員函數可以使用對象名或類名調用。
例2:
class A{
public:
static void f(A a);
private:
int x;
};
void A::f(A a){
//cout<<x;
cout<<a.x; //靜態成員函數訪問非靜態數據成員必須通過參數傳遞方式得到對象名,然後通過對象名訪問非靜態數據成員
}
附:函數訪問數據成員表
靜態成員函數與普通成員函數的根本區別在於:普通成員函數有 this 指針,可以訪問類中的任意成員;而靜態成員函數沒有 this 指針,只能訪問靜態成員(包括靜態成員變量和靜態成員函數)
二、友元
1.友元介紹
(1) 友元提供了不同類或對象的成員函數之間、類的成員函數與一般函數之間進行數據共享的機制。(破壞數據封裝和數據隱藏)
(2) 使用友元函數和友元類,使用關鍵字friend。(友元類的所有成員函數都自動成爲友元函數)
(3) 爲了確保數據的完整性,及數據封裝與隱藏的原則,建議儘量不使用或少使用友元。
2.友元函數
(1) 在類聲明中由關鍵字friend修飾的非本類的成員函數,在它的函數體中能夠通過對象名訪問private 和protected成員。
(2) 訪問對象中的成員必須通過對象名。
<1>普通函數聲明爲友元函數
#include<iostream>
using namespace std;
class Date;
class Time {
public:
Time(int h, int m, int s);
friend void display(Time &t);
private:
int hour, minute, second;
};
Time::Time(int h,int m,int s):hour(h),minute(m),second(s){}
void Time::display(Time &t) { //在友元函數體中能夠通過對象名訪問某類的private 和protected成員。
cout << t.hour << ":" << t.minute << ":" << t.second << endl;
}
int main() {
Time t1(20, 8, 30);
display(t1);
return 0;
}
<2>友元成員函數
#include<iostream>
using namespace std;
class Date;
class Time {
public:
Time(int h, int m, int s);
void display(Date &d);
private:
int hour, minute, second;
};
Time::Time(int h,int m,int s):hour(h),minute(m),second(s){}
class Date {
public:
Date(int m, int d, int y);
friend void Time::display(Date &d);
//來自Time類的函數是Date類的友元函數,因此在Date類中聲明friend,
//並將Date類對象傳參以便函數體訪問Date類私有數據。
private:
int month, day, year;
};
Date::Date(int m,int d,int y):month(m),day(d),year(y){}
void Time::display(Date &d) {
cout << d.year << "-" << d.month << "-" << d.day << endl; //display通過對象名訪問Date類的私有數據
cout << hour << ":" << minute << ":" << second << endl;
}
int main() {
Date d1(4, 10, 2020);
Time t1(20, 13, 14);
t1.display(d1);
return 0;
}
<3>一個函數可以被多個類聲明爲“朋友”,引用多個類中的私有數據
#include <iostream>
using namespace std;
class Date;
class Time{
public:
Time(int h,int m,int s){hour=h;minute=m;sec=s;}
friend void display(const Date &d,const Time &t);
private:
int hour,minute,int sec;
};
class Date {
public:
Date(int m,int d,int y){month=m;day=d;year=y;}
friend void display(const Date &d,const Time &t);
private:
int month,day,year;
};
void display(const Date &d,const Time &t){
cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl;
cout<<t.hour<<":"<<t.minute<<":"<<t.sec<<endl;
}
int main(){
Time t1(10,13,56);
Date d1(4,9,2020);
display(d1,t1);
return 0;
}
3.友元類
若B類爲A類的友元類,則B類的所有成員函數都是A類的友元函數,都能訪問對方類的private 和protected成員。(反之不成立)
#include <iostream>
using namespace std;
class Date;
class Time{
public:
Time(int h,int m,int s){hour=h;minute=m;sec=s;}
friend Date; //Date類爲Time類的友元類,Date對象能訪問Time所有成員
private:
int hour,minute,int sec;
};
class Date {
public:
Date(int m,int d,int y){month=m;day=d;year=y;}
void display(const Time &t);
private:
int month,day,year;
};
void display(cconst Time &t){
cout<<month<<"/"<<day<<"/"<<year<<endl;
cout<<t.hour<<":"<<t.minute<<":"<<t.sec<<endl;
}
int main(){
Time t1(10,13,56);
Date d1(4,9,2020);
d1.display(t1);
return 0;
}
注意:
(1) 友元關係是不能傳遞的,是單向的,是不被繼承的。
(2) 一般並不把整個類聲明爲友元類,只將需要的成員函數聲明爲友元函數.
(3) 友元破壞封裝原則,但有助於數據共享。不要過多使用友元,只有在使用它能使程序精煉,並大大提高程序的效率時才用友元。
三、用const修飾的類成員
1.常引用和常對象
常引用:被引用的對象不能被更新。
const 類型說明符 &引用名
常對象:對象必須進行初始化, 不能被更新, 不能調用普通成員函數
const 類名 對象名 或者 類名 const 對象名
例:常引用作形參
#include<iostream>
using namespace std;
void display(const double& r);
int main(){
double d(9.5);
display(d);
return 0;
}
void display(const double& r) {
//r=0; //報錯,因爲常引用做形參,在函數中不能更新r所引用的對象。
cout<<r<<endl;
}
例:常對象
#include <iostream>
using namespace std;
class Point {
public:
Point(int xx=0, int yy=0) {X=xx;Y=yy;cout<<"構造函數被調用"<<endl;}
void Setpoint(int x,int y){X=x;Y=y;}
int GetX() const {return X;}
int GetY() {return Y;}
private:
int X,Y;
};
void main(){
const Point A(4,5); //A 常對象
A.Setpoint(1,2); //非法調用普通成員函數和更新對象
cout<<A.GetY()<<endl; //非法調用普通成員函數
}
2.常數據成員
(1) 使用const說明的數據成員。
(2) 常數據成員只能用初始化列表的形式賦初值。
(3) 可以被const成員函數引用,也可以被非const成員函數引用
(4) 常對象的數據成員都是常數據成員,因此常對象構造函數只能用參數初始化表對常數據成員進行初始化。
#include<iostream>
using namespace std;
class A{
public:
A(int i);
void print();
const int &r; //常引用,不能被更新
private:
const int a; //常數據成員
static const int b; //靜態常數據成員
};
const int A::b=10; //靜態常數據成員類定義初始化
A::A(int i):a(i),r(a) {} //構造函數裏用初始化列表給常數據成員a和r賦初值
void A::print()
{ cout<<a<<":"<<b<<":"<<r<<endl;
}
void main(){
A a1(100),a2(0);
a1.print(); // 100:10:100
a2.print(); // 0:10:0
}
2.常成員函數
(1) 類型說明符 函數名(參數表)const;
(2) 常成員函數只能引用本類的所有數據成員(包括const數據成員,也可引用非const數據成員),不能修改它們,也不能調用該類中沒有用const修飾的成員函數。
(3) 常對象只能調用它的常成員函數。
(4) const關鍵字可以被用於參與對重載函數的區分。
(5) const成員函數聲明和定義處都要加上const
(6) 函數開頭的const用來修飾函數的返回值,表示返回值是const類型,是不可以被修改的一塊內存.
例如:const get_char();
#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;
}
void main(){
R a(5,4);
a.print(); // 5:3
const R b(20,52); //常對象
b.print(); // 20:52 函數重載
}
注意:
- 如果一個類的有些數據成員值允許改變,另一些數據成員的值不允許改變,則可以將一部分數據成員聲明爲const。可以用非const的成員函數引用這些數據成員的值,修改非const數據成員的值。
- 如果要求所有的數據成員的值都不允許改變,則可以將對象聲明爲const,再用const成員函數引用數據成員,起到“雙保險”的作用。
- 常對象只能調用其中const成員函數。如果需要訪問常對象中的數據成員,可將常對象中所有成員函數都聲明爲const成員函數,但必須確保在函數中不修改對象中的數據成員。