4.2.8靜態(tài)成員
靜態(tài)成員變量
1.所有對象共享同一份數(shù)據(jù);
2.在編譯階段分配內(nèi)存;
3.類內(nèi)聲明,類外初始化;
示例:
class Person
{
public:
public:
static int _A;
//靜態(tài)成員變量,不屬于某個對象上,所有對象都共享同一個數(shù)據(jù)
//靜態(tài)成員變量具有兩種訪問方式
//1.通過對象進行訪問
//2.通過類名進行訪問
//靜態(tài)成員變量也是有訪問權(quán)限的
private:
static int _B;//類內(nèi)聲明
};
int Person::_A=100;
int Person::_B = 200;//類外初始化
void test02()
{
//通過類名訪問靜態(tài)成員變量
cout << Person::_A << endl;
//cout << Person::_B << endl;類外訪問不到私有(保護)靜態(tài)成員變量
}
靜態(tài)成員函數(shù)
1.所有對象共享同一個函數(shù);
2.靜態(tài)成員函數(shù)只能訪問靜態(tài)成員變量;
示例:
class Person
{
public:
static void func()//靜態(tài)成員函數(shù)也是有訪問權(quán)限的
{
_A = 100;//靜態(tài)成員函數(shù)可以訪問 靜態(tài)成員變量
//_B = 200;//靜態(tài)成員函數(shù)不可以訪問 非靜態(tài)成員變量
? ? ? ? //因為無法區(qū)分到底是哪個對象的_B
cout << "static void func()" << endl;
}
public:
static int _A;
int _B;
private:
static void func2()
{
cout << "static void func2()" << endl;
}
};
int Person::_A = 0;
void test01()
{
Person p;
p.func();//1.通過對象訪問
cout << "-------" << endl;
Person::func();//2.通過類名訪問
}
void test02()
{
//Person::func2();//類外訪問不到私有的靜態(tài)成員函數(shù)
}
4.3C++對象模型和this指針
4.3.1成員變量和成員函數(shù)分開存儲
只有非靜態(tài)成員變量才屬于類的對象上
4.3.1this指針
每個非靜態(tài)成員函數(shù)只會誕生一份函數(shù)實例,也就是說多個同類型的對象會共用一塊代碼
C++通過提供特殊的對象指針,this指針,能夠使得代碼區(qū)分調(diào)用自己的對象
概念:
1.this指針指向被調(diào)用的成員函數(shù)所屬的對象;
2.this指針是隱含每一個非靜態(tài)成員函數(shù)的一種指針;
3.this指針不需要定義,直接使用即可;
用途:
1.當形參和成員變量同名時,可用this指針來區(qū)分;(解決名稱沖突)
2.在類的非靜態(tài)成員函數(shù)中返回對象本身,可使用 return *this;
示例:
class Person
{
public:
Person(int age)
{
//1.解決名稱沖突
//age = age;//error ,編譯器認為兩個age是同一個,即形參age
//this指針指向的是被調(diào)用成員函數(shù)所屬的對象
this->age = age;
}
void PersonAddAge(Person &p)
{
this->age += p.age;
}
//易錯點
//Person PersonAddAge2(Person &p)
//error
Person & PersonAddAge2(Person &p)
{
this->age += p.age;
//this是指向p2的指針,而*this就是p2這個對象本體
return *this;
}
Person? PersonAddAge3(Person &p)
{
this->age += p.age;
//this是指向p2的指針,而*this就是p2這個對象本體
return *this;
}
int age;
};
void test01()
{
Person p1(18);
cout << "the age of p1 is " << p1.age << endl;
}
//2.返回對象本身用*this
void test02()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge(p1);
cout << "the age of p2 is " << p2.age << endl;
//p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
//error
}
void test03()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge2(p1);
cout << "the age of p2 is " << p2.age << endl;
//鏈式編程思想
p2.PersonAddAge2(p1).PersonAddAge2(p1).PersonAddAge2(p1);
cout << "the age of p2 is " << p2.age << endl;
}
void test04()
{
Person p1(10);
Person p2(10);
p2.PersonAddAge3(p1);//p2.age更改,但函數(shù)返回的是p2的拷貝
? ? ? ? ? ? ? ? ? ? //此時p2.age=20
cout << "the age of p2 is " << p2.age << endl;
//第一個函數(shù)更改了p2.age 但返回的是p2的拷貝 記為 p2’
//第二個函數(shù)更改的是p2’
//第三個函數(shù)更改的是p2''
//所以p2.age=30
p2.PersonAddAge3(p1).PersonAddAge3(p1).PersonAddAge3(p1);
cout << "the age of p2 is " << p2.age << endl;
}
4.3.3空指針訪問成員函數(shù)
C++中空指針也可以調(diào)用成員函數(shù),但要注意有沒有用到this指針
如果用到this指針,需要加以判斷保證代碼健壯性
示例:
class Person
{
public:
//空指針可以正常訪問
void showClassName()
{
cout << "this is Person class" << endl;
}
void showPersonAge()
{
//報錯原因是因為傳入的指針為NULL
cout << "age is " << m_Age << endl;
//m_Age默認為this->m_Age
}
void showPersonAge2()
{
//提高代碼健壯性
if (this == NULL)
{
return;
}
cout << "age is " << m_Age << endl;
}
int m_Age;
};
void test01()
{
Person *p = NULL;
p->showClassName();
//p->showPersonAge();
//error
p->showPersonAge2();
}
4.3.4const修飾成員函數(shù)
常函數(shù):
1.成員函數(shù)后加const后我們稱這個函數(shù)為常函數(shù);
2.常函數(shù)內(nèi)不可以修改成員屬性;
3.成員屬性聲明時加關(guān)鍵字mutable后,在常函數(shù)中依然可以修改
常對象:
1.聲明對象前加const稱該對象為常對象;
2.常對象只能調(diào)用常函數(shù);
示例:
class Person
{
public:
void showPerson1()
{
//this指針本質(zhì)是指針常量,指針的指向是不可以修改的
//Person * const this;
this->m_A = 100;//this指針指向的內(nèi)容可以修改
//this = NULL;//this指針不可以修改指針指向
}
//在成員函數(shù)后面加上const 修飾的是this指針,讓指針指向的值也不可以修改
void showPerson2() const? //常函數(shù)
{
//如果想要this指針指向的值也不可以修改
//把const加在成員函數(shù)后
//就相當于const Person * const this;
? ? //this->m_A = 100;//error
this->m_B = 100;//ok
}
public:
int m_A;
mutable int m_B;//特殊變量,即使在常函數(shù)中,也可以修改這個值
? ? ? ? ? ? ? ? //加關(guān)鍵字mutable
};
void test01()
{
//常對象
const Person p;
//p.m_A = 100;//error
p.m_B = 100;//ok ,m_B是特殊值,在常對象下也可以修改
? ? //常對象只能調(diào)用常函數(shù)
//不可以調(diào)用普通成員函數(shù),因為普通成員函數(shù)可以修改屬性
//p.showPerson1();//error
p.showPerson2();//ok
}
4.4友元
目的:讓一個函數(shù)或類 訪問另一個類中的私有成員
關(guān)鍵字:friend
友元的三種實現(xiàn):
1.全局函數(shù)做友元函數(shù)
2.類做友元類
3.類的成員函數(shù)做友元
4.5運算符重載
概念:對已有的運算符重新進行定義,賦予其另一種功能,以適應(yīng)不同的數(shù)據(jù)類型
4.5.1加號運算符重載
作用:實現(xiàn)兩個自定義數(shù)據(jù)類型相加的運算
兩種方式:
1.成員函數(shù)重載
2.全局函數(shù)重載
示例:
class Person
{
public:
//Person operator+(Person &p);//1.成員函數(shù)重載
Person(){}
Person(int a, int b);
public:
int m_A;
int m_B;
};
//Person Person::operator+(Person &p)
//{
// Person temp;
// temp.m_A=this->m_A + p.m_A;
// temp.m_B=this->m_B + p.m_B;
// return temp;
//}
Person::Person(int a, int b)
{
m_A = a;
m_B = b;
}
//2.全局函數(shù)重載
Person operator+(Person &p1, Person &p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
//運算符重載,也可以發(fā)生函數(shù)重載
//函數(shù)重載的版本
Person operator+(Person &p1, int a)
{
Person temp;
temp.m_A = p1.m_A + a;
temp.m_B = p1.m_B + a;
return temp;
}
void test00()
{
Person p1(10, 10);
Person p2 = p1 + 10;
cout << "p2.m_A=" << p2.m_A << endl
<< "p2.m_B=" << p2.m_B << endl;
}
void test01()
{
Person p1(10, 10);
Person p2(20, 20);
Person p3 = p1 + p2;
//1.成員函數(shù)重載的本質(zhì)調(diào)用
//Person p3=p1.operator+(p2);
//2.全局函數(shù)重載的本質(zhì)調(diào)用
//Person p3=operator+(p1,p2);
}
總結(jié):
1.對于內(nèi)置的數(shù)據(jù)類型的表達式的運算符是不可能改變的;
2.不要濫用運算符重載;
4.5.2左移運算符重載
作用:輸出自定義數(shù)據(jù)類型
注意:只能通過全局函數(shù)重載輸出流運算符,且需要設(shè)置為類的友元函數(shù)
示例:
class Person
{
//將全局函數(shù)設(shè)置為友元
friend ostream & operator<<(ostream &os, Person &p);
public:
//利用成員函數(shù)重載 左移運算符 p.operator<<(cout) 簡化版本 p<<cout
//不會利用成員函數(shù)重載<<運算符 ,因為無法實現(xiàn)cout在左側(cè)
//void operator<<(cout ){}
? ? Person(int a, int b)
{
m_A = a;
m_B = b;
}
private:
int m_A;
int m_B;
};
//只能利用全局函數(shù)重載左移運算符
//void? operator<<(ostream &cout,Person &p)//本質(zhì) operator<<(cout,p) 簡化 cout<<p
//{
// cout << "m_A=" << p.m_A << endl
// << "m_B=" << p.m_B << endl;
//}
//ostream 也是一種類
//ostream對象只能有一個,所以需要加&
ostream & operator<<(ostream &os, Person &p)//本質(zhì) operator<<(cout,p) 簡化 cout<<p
{
os << "m_A=" << p.m_A <<"? "
<< "m_B=" << p.m_B ;
return os;
}
void test01()
{
Person p(10,10);
//cout << p ;
cout<<p<<endl;
//返回值為void時,上一行程序會崩掉
//由于左側(cè)返回的是void ,不能實現(xiàn)鏈式編程
//因此需要對左移運算符重載函數(shù)進行返回值的修改
}
總結(jié):重載左移運算符配合友元可以實現(xiàn)輸出自定義數(shù)據(jù)類型