this指針和常成員函數
this指針
- 類中的構造函數和成員函數都隱藏了一個該類類型的指針參數,名爲this;在構造函數或者成員訪問類中的其它成員時,本質都是通過this指針實現的。
對於構造函數,this指針指向正在創建的對象;
對於成員函數,this指針指向該函數的調用對象。 - 顯示使用this指針的場景
- 區分作用域;
- 從成員函數返回調用對象自身;
- 從類的內部銷燬對象的自身;
- 作爲函數的實參,實現對象間的交互。
代碼示例
- this.cpp
#include <iostream>
using namespace std;
class User{
public:
/*User(const string& name,int age)
:m_name(name),m_age(age){
cout << "構造函數:" << this << endl;
}*/
//使用this指針區分成員變量和參數變量
User(const string& m_name,int m_age){
this->m_name = m_name;
this->m_age = m_age;
}
void print(void){
cout << "我叫" << this->m_name <<
",今年" << this->m_age << "歲"
<< endl;
}/*編譯器編譯以後:
void print(User* this){
cout << this->m_name << ..
<< this->m_age << ..
}*/
private:
string m_name;
int m_age;
};
int main(void)
{
User u1("張三",22);
cout << "&u1=" << &u1 << endl;
User u2("李四",23);
cout << "&u2=" << &u2 << endl;
u1.print();//User::print(&u1);
u2.print();//User::print(&u2);
return 0;
}
- 執行結果
* 02this.cpp
#include <iostream>
using namespace std;
class Counter{
public:
Counter(int count=0):m_count(count){}
Counter& add(void){
++m_count;
//this指向調用對象
//*this就是調用對象自身
return *this;//返回自引用
}
void destroy(void){
//...
cout << "this=" << this << endl;
delete this;//對象自銷燬
}
int m_count;
};
int main(void)
{
Counter cn;
cn.add().add().add();
cout << cn.m_count << endl;//3
Counter* pcn = new Counter;
//...
cout << "pcn=" << pcn << endl;
//delete pcn;
pcn->destroy();
return 0;
}
- 執行結果
* 03this.cpp
#include <iostream>
using namespace std;
class Student;//短視聲明
class Teacher{
public:
//向參數表示的學生對象提問
void educate(Student* s);
//獲取學生對象回答的答案
void reply(const string& answer);
private:
string m_answer;//保存答案
};
class Student{
public:
//接收問題,並將答案傳回給教師對象
void ask(
const string& question,Teacher* t);
};
void Teacher::educate(Student* s){
s->ask("什麼是this指針?",this);
cout << "學生回答:" << m_answer << endl;
}
void Teacher::reply(const string& answer){
m_answer = answer;
}
void Student::ask(
const string& question,Teacher* t){
cout << "問題:" << question << endl;
t->reply("this指針指向調用對象的地址");
}
int main(void)
{
Teacher teacher;
Student student;
teacher.educate(&student);
return 0;
}
- 執行結果
常成員函數(常函數)
- 在一個成員函數參數表後面加const修飾,表示這個函數是常成員函數。
返回類型 函數名(形參表) const{函數體} - 常函數中的this指針是一個常指針,不能再常函數中修改成員變量的值。(注:被mutable關鍵字修飾的成員變量,可以在常函數中被修改)
- 非常對象既可以調用常函數也可以調用非常函數,但是常對象只能調用常函數,不能調用非常函數。(注:常對象也包括常指針或常引用)
- 函數名和形參相同的成員函數,其常版本和非常版本可以構成重載關係,常對象調用常數版本,非常對象調用非常版本。
代碼示例
- constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
A(int data = 0):m_data(data){}
void print(void) const {//常函數
cout << m_data++ << endl;
}/*
void print(const A* this){
cout << this->m_data++ << endl;
}*/
private:
mutable int m_data;
};
int main(void)
{
A a(100);
a.print();//100
a.print();//101
a.print();//102
return 0;
}
- 執行結果
- 02constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
//void func1(const A* this)
void func1(void) const {
cout << "常函數" << endl;
}
//void func2(A* this)
void func2(void){
cout << "非 常函數" << endl;
}
};
int main(void)
{
A a;
a.func1();//A::func1(&a),A*
a.func2();
const A a2 = a;
a2.func1();//A::func1(&a2),const A*
//a2.func2();//error
const A* pa = &a;//pa常指針
pa->func1();
//pa->func2();//error
const A& ra = a;//ra常引用
ra.func1();
//ra.func2();//error
return 0;
}
- 執行結果
- 03constfunc.cpp
#include <iostream>
using namespace std;
class A{
public:
void func(void)const{//const A* this
cout << "func常版本" << endl;
}
void func(void){//A* this
cout << "func非常版本" << endl;
}
};
int main(void)
{
A a;
a.func();//非常版本
const A& ra = a;
ra.func();//常版本
return 0;
}
- 執行結果
析構函數(Destructor)
- 語法
class 類名{
~類名(void){
負責清理對象生命期內動態分配的資源
}
};
1)函數名必須是 “~類名”
2)沒有返回類型,也沒有參數
3)不能被重載,一個類只能有一個析構函數
- 當對象被銷燬時,該對象的析構函數自動被調用和執行;
- 1)棧對象離開作用域時,其析構函數被作用域終止右花括號“}”調用;
- 2)堆對象的析構函數被delete操作符調用。
- 如果一個類沒用顯示定義析構函數,那麼編譯器會爲這個類提供一個缺省的析構函數;
- 1)對基類類型的成員變量,什麼也不做;
- 2)對類 類型成員變量(成員子對象),會自動調用相應類的析構函數。
- 對象的創建和銷燬過程
- 1)對象創建
–》分配內存
–》構造成員子對象(按聲明順序)
–》執行構造函數代碼 - 2)對象銷燬
–》執行析構函數代碼
–》析構成員子對象(按聲明逆序)
–》釋放內存
- 1)對象創建
代碼示例
- destructor.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int data = 0)
:m_data(new int(data)){
//m_data = new int(data);
}
void print(void)const{
cout << *m_data << endl;
}
~Integer(void){
cout << "Integer的析構函數" << endl;
delete m_data;
m_data = NULL;
}
private:
int* m_data;
};
int main(void)
{
if(1){
Integer i(100);
i.print();//100
cout << "test1" << endl;
Integer* pi = new Integer(200);
pi->print();//200
delete pi;//delete-->調用析構
cout << "test3" << endl;
}//}-->調用析構函數
cout << "test2" << endl;
return 0;
}
-
執行結果
-
02destructor.cpp
#include <iostream>
using namespace std;
class A{
public:
A(void){
cout << "A::A(void)" << endl;
}
~A(void){
cout << "A::~A(void)" << endl;
}
};
class B{
public:
B(void){
cout << "B::B(void)" << endl;
}
~B(void){
cout << "B::~B(void)" << endl;
}
A m_a;//成員子對象
};
int main(void)
{
B b;
return 0;
}
- 執行結果
拷貝構造和拷貝賦值
淺拷貝和深拷貝
- 如果一個類中包含指針形式的成員變量,缺省的拷貝構造函數只是複製指針變量本身,而沒用複製該指針所指向的內容,這種拷貝方式稱爲淺拷貝。
- 淺拷貝將導致不同對象之間的數據共享,如果數據在堆區,析構時還可能發送“double free”,導致進程的終止,這時需要定義一個支持複製指針指向內容的拷貝構造函數,即深拷貝。
拷貝賦值
- 當兩個對象進行賦值操作時,比如“i3 = i2”,編譯器會將其自動翻譯成 i3.operator=(i2)成員函數的調用形式,其中“operator=”稱爲拷貝賦值操作符函數,通過它實現兩個對象的賦值操作,該函數的返回結果就是表達式結果。
- 但是編譯器缺省提供的拷貝賦值函數和缺省的拷貝構造函數類似,也就是淺拷貝,只是複製了指針變量本身,沒用複製指針所指向的內容,有“double free”和內存泄漏的風險。
- 爲了得到深拷貝的效果,避免錯誤,必須自己定義一個支持深拷貝的拷貝賦值函數。
類名& operator=(const 類名& that){
if(&that != this){//防止自賦值
釋放舊內存;
分配新內存;
拷貝新數據;
}
return *this;//返回自引用
}
this指向調用對象(左操作數)
that對應參數對象(右操作數)
代碼示例
- copy.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int data = 0)
:m_data(new int(data)){
//m_data = new int(data);
}
void print(void)const{
cout << *m_data << endl;
}
~Integer(void){
cout << "Integer的析構函數" << endl;
delete m_data;
m_data = NULL;
}
/*編譯器提供的缺省拷貝構造函數(淺拷貝)*/
/*Integer(const Integer& that){
cout << "缺省的拷貝構造" << endl;
m_data = that.m_data;
}*/
/*自定義深拷貝構造函數*/
Integer(const Integer& that){
cout << "自定義深拷貝" << endl;
//m_data = new int;
//*m_data = *that.m_data;
m_data = new int(*that.m_data);
}
private:
int* m_data;
};
int main(void)
{
Integer i1(100);
Integer i2 = i1;//拷貝構造
i1.print();//100
i2.print();//100
return 0;
}
- 執行結果
- 實現string類的基本,string.cpp
#include <iostream>
#include <cstring>
using namespace std;
class String{
public:
//構造函數
String(const char* str){
m_str = new char[strlen(str)+1];
strcpy(m_str,str);
}
//練習:析構函數,拷貝構造
~String(void){
delete[] m_str;
m_str = NULL;
}
String(const String& that){
m_str =
new char[strlen(that.m_str)+1];
strcpy(m_str,that.m_str);
}
const char* c_str(void)const{
return m_str;
}
private:
char* m_str;
};
int main(void)
{
String s("hello");
cout << s.c_str() << endl;//hello
String s2 = s;
cout << s2.c_str() << endl;//hello
return 0;
}
- 執行結果
- 拷貝賦值,copy.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(int data = 0)
:m_data(new int(data)){
//m_data = new int(data);
}
void print(void)const{
cout << *m_data << endl;
}
~Integer(void){
cout << "Integer的析構函數" << endl;
delete m_data;
m_data = NULL;
}
/*編譯器提供的缺省拷貝構造函數(淺拷貝)*/
/*Integer(const Integer& that){
cout << "缺省的拷貝構造" << endl;
m_data = that.m_data;
}*/
/*自定義深拷貝構造函數*/
Integer(const Integer& that){
cout << "自定義深拷貝" << endl;
//m_data = new int;
//*m_data = *that.m_data;
m_data = new int(*that.m_data);
}
/*編譯器缺省實現的拷貝賦值操作符函數*/
//i3 = i2;//i3.operator=(i2)
/*
Integer& operator=(const Integer& that){
cout << "缺省拷貝賦值函數" << endl;
m_data = that.m_data;
return *this;
}*/
//自定義深拷貝賦值操作符函數
Integer& operator=(const Integer& that){
cout << "自定義深拷貝賦值" << endl;
if(&that != this){
delete m_data;
m_data = new int;
*m_data = *that.m_data;
}
return *this;
}
private:
int* m_data;
};
int main(void)
{
Integer i1(100);
Integer i2(i1);//拷貝構造
i1.print();//100
i2.print();//100
Integer i3;
//i3.operator=(i2);
i3 = i2;//拷貝賦值
i3.print();//100
return 0;
}
- 執行結果