C++:随笔5---this指针和类的继承
this指針:(通過一個典型的例子來認識他)
class Human
{char fishc;Human(char fishc);//構造函數};
Human::Human(char fishc)//對構造函數進行初始化
{fishc=fishc;//意圖就是把這個傳入參數賦值給這個上邊類屬性的fishc
}
//但是這樣賦值的話,他們的名字一樣,這樣的話構造器就有可能認不出來,(因為他不知道你是要把屬性去覆蓋參數,還是把傳入的參數去覆蓋給屬性,因為兩者的名字一樣,但是語法沒有錯。他們是兩個不同區域的一個是傳入的參數,一個是類的屬性,(因為他們位于兩個不同的區域,所以語法上沒有錯))//所以怎么讓構造器知道哪個是參數哪個是屬性呢?這個時候就需要用到this指針。
this->fishc=fishc;//this指針是指向當前的類生成的對象//所以就很明顯前者是類的屬性,后邊是參數。
//這樣的話編譯器就懂了,賦值操作符的左邊將被解釋為當前對象的fishc屬性,右邊將被解釋為構造器的傳入來的fishc的參數。
//PS:注意使用this指針的基本原則是如果代碼不存在二義性隱患,就不必使用this指針。
------------類的繼承-------
繼承機制使得程序員可以創建一個類的堆疊層次結構,每個子類均將繼承在他的基類里定義的方法和屬性。(在繼承原有方法的同時,再增加另外的一些屬性的方法)(通過繼承機制可以對現有的代碼進行進一步的擴展,并應用在新的程序中)
(1)他們都是動物的子類。我們可以編寫一個Animal作為Turtle和Pig的基類。
繼承類的語法:
//語法:
class SubClass:public Superclass{...}
//例子:
class Pig:public Animal{...}
基類是可以派生出其他的類,也稱為父類和超類;子類是從基類派生出來的類(比如Turtle類和Pig類)。
??
實現如下:
#include<iostream>
#include<string>
class Anmial//聲明一個基類
{public:std::string mouth;void eat();void sleep();void drool();
};
//繼承基類
class Pig:public Animal
{public:void climb();
};
class Turtle:public Animal
{public:void swim();
};
//對上述方法進行補充
void Animal::eat()
{std::cout<<"我正在吃"<<std::endl;
}
void Animal::sleep()
{std::cout<<"我正在睡覺"<<std::endl;
}
void Animal::eat()
{std::cout<<"我正在流口水"<<std::endl;
}//上述把基類的三個方法給搞定了
//下面實現子類的方法(不管他們是基類還是父類,他們都是方法就按照類的方法實現來寫)
void Pig::climb()
{std::cout<<"我會爬樹"<<std::endl;
}
void Turtle::swim()
{std::cout<<"我會游泳"<<std::endl;
}
int main()
{//對類進行實例化Pig pig;Turtle turtle;pig.sleep();turtle.sleep();pig.climb();turtle.swim();}
------帶參數的構造器----
//構造器帶著輸入參數
//1、聲明
class Animal
{public:Animal(std::string thename);std::string name;
};
class Pig:public Animal
{public:Pig(std::string thename);};
//2、方法定義
Animal::Animal(std::string thename)
{name=thename;
}
Pig::Pig(std::string thename):Animal(thename)//Pig的構造器繼承Animal的構造器
{}
因為Pig的方法里面是空的,他是直接繼承于Animal的方法的。
------訪問控制--------
上述類的所有成員都是用public:語句聲明。
所謂的訪問控制就是c++提供了一種用來保護類里的方法和屬性的手段。
??
-------覆蓋------
(既有共同特征又需要在不同類里有不同的實現方法)意思就是在類里邊重新聲明一下這個方法,他的函數名字參數返回值一摸一樣(不然的話就會變成重載了)。
例子:為Animal添加eat方法,并在子類中進行覆蓋。
-------重載方法--------
繼承之后不能重載。(子類里邊不能重載)
----友元-------
? ?
聲明另外一個類是這個類的友元類,友元關系是類之間的一種特殊關系,這種特殊關系不僅允許友元類訪問對方的public方法和屬性,還允許友元訪問對方的protected和private方法和屬性。
聲明一個友元關系的語法,在類聲明里的某個地方加上一條friend class **就行了(其中**表示你需要友元的另一個類的那個名字)。
注意這條語句可以放在任何地方,放在public,protected和private段落里邊都可以。(這樣之后他們兩個人就相當于是同一個人)
例子:定義Lovers這個類,有兩個子類Boyfriend和Girlfriend。Lovers類的方法kiss()和ask();第三者Others類,想要kiss()Girlfriend類的對象。
#include<stdio.h>
#include<iostream>class Lovers
{public:Lovers(std::string theName);//構造函數,就是給他命名void kiss(Lovers* lover);//參數聲明一個指針,void ask(Lovers* lover,std::string something);protected:std::string name;friend class others;//這時候others跟Lovers就是一對。
};
class Boyfriend:public Lovers
{public:Boyfriend(std::string theName);
};
class Girlfriend:public Lovers
{public:Girlfriend(std::string theName);
};
class Others
{public:Others(std::string theName);void kiss(Lovers* lover);protected:std::string name;//Lover->name屬于這個類的protected
};
Lovers::Lovers(std::string theName);
{name=theName;//構造函數給它命名,也就是說我們造一個人出來的時候,
}
void Lovers::kiss(Lovers* lover)
{std::cout<<name<<"親親"<<lover->name<<std::endl;
}
void Lovers::ask(Lovers* lover,std::string something)
{std::cout<<Lover->name<<"幫我"<<something<<std::endl;
}
//子類
Boyfriend::Boyfriend(std::string name):Lovers(theName)
{}
Girlfriend::Girlfriend(std::string name):Lovers(theName)
{}
//友元類
Others::Others(std::string theName);
{name=theName;//構造函數給它命名,也就是說我們造一個人出來的時候,
}void Others::kiss(Lovers* lover)//這個里邊的參數引用到了lover
{std::cout<<name<<"吻一下"<<lover->name<<std::endl;//lover->name屬于這個Lovers類的protected,不是這個類的子類根本訪問不到,而others不是它的子類,所以理論上是訪問不到的,這里因為上邊是friend class others;所以可以訪問到。
}
//主函數
int main()
{Boyfriend boyfriend("A君");//造一個人出來給它命名叫做A君(構造函數)Girlfriend girlfriend("B妞");//叫做B妞Others others("路人甲");//others這個陌生人叫做路人甲girlfriend.kiss(&boyfriend);//因為參數是指針所以傳進去的應該是對象的地址girlfriend.ask(&boyfriend,"拖地")std::cout<<"傳說中的友元類,路人甲出現了\n"<<std::endl;others.kiss(&girlfriend);return 0;
}
-------靜態屬性和靜態方法-------
創建一個靜態屬性和靜態方法:只需要在他的聲明前加上static保留字即可。
#include<string>
#include<iostream>class Pet
{public:Pet(std::string theName);//帶參數的構造器~Pet();static int getCount();//這里作為一個接口,用來給他由他生成的對象來獲取他這個計數器protected:std::string name;private://private是這個類里的方法才能夠調用的,getCount()函數可以調用//private成員只有這個類里邊的方法才能夠訪問它static int count;//私有成員,一個count計數器
};
class Dog :public Pet//繼承Pet類
{public:Dog(std::string theName);~Dog();
};
class Cat :public Pet//繼承Pet類
{public:Cat(std::string theName);~Cat();
};
int Pet::count = 0;//這一句做了兩件事;第一:讓編譯器為count這個變量的值分配內存;第二:這個變量因為是在靜態存儲區,他是把這個變量初始化為0;
Pet::Pet(std::string theName)//構造函數,當該構造函數被調用的時候說明這個寵物已經被建造出來了
{name = theName; count++;//那么這個計數器就加一std::cout << "一只寵物變出來了,名字叫做:" << name << std::endl;
}
Pet::~Pet()//析構器
{count--; //析構器說明這個寵物已經掛掉了,所以計數器要減掉std::cout << name << "掛掉了" << std::endl;
}
int Pet::getCount()//這個接口函數getCount()的唯一作用,把這個count返回出來,把他拿到的這個私有成員返回出來,然后show出來
{return count;//是為了任何代碼都可以通過調用getCount()函數(因為是public)從而來讀取該屬性count的值(因為該值是private,所以對該屬性只能是讀不能是寫,只有在Pet的析構函數和構造函數才能夠對他進行寫)
}
Dog::Dog(std::string theName) :Pet(theName)//Dog的構造器繼承Pet的構造器
{//std::cout<<"this:"<<this<<endl;//1生成一個dog對象之后就把this指針給打印出來//按此它生成一個dog對象的時候this指針應該是指向dog對象的。下邊生成dog對象
}
Dog::~Dog()
{}
Cat::Cat(std::string theName) :Pet(theName)
{}
Cat::~Cat()
{}
int main()
{Dog dog("Tom");//整了一只狗dog叫做Tom(繼承構造器的實現)//用Dog類生產出dog對象Cat cat("Jerry");//整了一只貓叫做Jerrystd::cout<<"dog:"<<&dog<<std::endl;//2這里生成dog對象之后,我們把dog也給打印出來std::cout << "已經誕生了" << Pet::getCount() << "只寵物!\n\n";//顯示出來//getCount()函數調用私有成員count{//注意這里的大括號,是有作用的。這里相當于是一個區域Dog dog_2("Tom_2");//又整了一只狗Cat cat_2("Jerry_2");//又整了一只貓std::cout << "現在呢,已經誕生了" << Pet::getCount() << "只寵物!\n\n";}//到此之前還剩下兩只,因為大括號之后析構函數了,把寵物給毀掉std::cout << "\n現在還剩下" << Pet::getCount() << "只寵物!\n\n";return 0;//return 0的時候一只寵物都沒有了,因為析構函數了,把寵物給毀掉
}
//上述1和2處打印的地址完全一樣
結果:右邊結果是去掉main函數中間的大括號的
? ?
static靜態變量的詳解:https://www.cnblogs.com/dc10101/archive/2007/08/22/865556.html
靜態成員是所有對象共享的,所以不能在靜態方法里訪問非靜態的元素。
非靜態方法可以訪問類的靜態成員,也可以訪問類的非靜態成員。
C++內存分配方式詳解——堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區:https://fishc.com.cn/blog-9-1097.html
this指針:this指針是類的一個自動生成,自動隱藏的私有成員,它存在于類的非靜態成員函數中,指向被調用函數所在的對象的地址。(也就是說我生成每一個對象的時候,它都會自動生成一個this指針,這個指針指向的是對象它的一個地址)當一個對象被創建時,該對象的this指針就會自動指向對象數據的首地址。
#include<iostream>
class Point
{private:int x,y;public:point(int a,intb){x=a;y=b}void MovePoint(int a,int b){x=a;y=b;}void print(){ std::cout<<"x="<<x<<"y="<<y<<endl;}
};
int main()
{Point point1(10,10);//用Point這個類生產出Point1這個對象。這個點的坐標在(10,10)這個位置Point1.MovePoint(2,2);point1.print();return 0;
}
//當對象point1調用MovePoint這個成員的時候,(實時上我們生成point1這個對象的時候,就有一個this指針指向point1的地址),當他調用MovePoint這個函數的時候,即將point1對象的地址傳遞給this指針,(因為我們需要用到的是point1這個對象的成員,那我們就必須知道這個對象的地址,就必須傳遞給this指針)
//MovePoint函數的原型事實上應該是 void MovePoint(Point* this,int a,int b);他有三個參數,一個是隱含的this指針它指向Point對象(第一個參數是指向該類對象的一個指針,我們在定義成員函數時沒看見是因為這個參數在類中是隱含的,是C++的一個規則,因為每一個都是默認添加進第一個規則)。
//這樣子pOint1的地址就傳遞給this,所以在MovePoint()函數中便可以顯示的寫成void MovePoint(int a,int b)(this->x=a;this->y=b)(其實也可以x=a,這樣編譯器知道哪一個是參數哪一個是變量,用this做區分)
//我們這樣就可以知道point1調用該函數后,也就是point1的數據成員被調用并更新了值。
?
總結
以上是生活随笔為你收集整理的C++:随笔5---this指针和类的继承的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++:随笔4--对象
- 下一篇: c++成员函数的重载、覆盖、隐藏区别