C++類與物件(1)
類的設計:可以把屬性和行為放在不同的權限下
struct和class區別在于某人的訪問權限不同
- struct:默認共有
- class:默認私有
物件的初始化和清理
如果我們不寫,系統會自己給我沒寫
- 建構式的語法 類名(){}
沒有回傳值,也不寫void
函式名和型別相同
可以有參,也可以無參
在呼叫物件會自動呼叫函式,無需手動呼叫,只呼叫一次
- 解構式語法 ~類名(){}
同上,不過是無參(不可以多載),銷毀時自動呼叫函式,只呼叫一次
建構式的分類與呼叫
分類
- 有參和無參(默認)
- 普通和拷貝
拷貝函式
函式名(const 函式名 &p(物件)){
age = p.age;
}
#include<iostream> //建構式和解構式
using namespace std;
class person{
public:
int age;
public:
//建構式
//普通
person(){
cout << "Person無參建構式的呼叫" << endl;
}
person(int a){
age = a;
cout << "Person有參建構式的呼叫" << endl;
}
//拷貝
person(const person &p){
age = p.age;
cout << "Person拷貝建構式的呼叫" << endl;
}
//解構式
~person(){
cout << "Person解構式的呼叫" << endl;
}
};
void test01(){
//呼叫
//1.括號法
//person p1;
//person p2(10);
//person p3(p2);
//注:呼叫默認建構式時,不要加()(系統會以為是一個函式的宣告)
//cout << "p2的年齡:" << p2.age << endl;
//cout << "p3的年齡:" << p3.age << endl;
//2.顯示法
person p4;
person p5 = person(10);//右值:匿名物件,當前行結束,系統識訓匿名物件
person p6 = person(p5);
//注:不要利用拷貝函式初始化匿名物件
//3.隱式轉換法
person p7 = 10;//相當于person p5 = person(10);
person p8 = p7;
}
int main(){
test01();
return 0;
}*/
拷貝函建構式的呼叫時機
c++中呼叫拷貝函式一般三種請況
- 使用一個已經創建完畢的物件來初始化一個物件
- 值傳遞的方法給函式引數傳值
- 以值方式回傳區域物件
#include<iostream> //拷貝時機
using namespace std;
class Person{
private:
int m_Age;
public:
Person(){
cout << "person的默認無參建構式呼叫" << endl;
}
Person(int age){
cout << "Person有參建構式呼叫" << endl;
m_Age = age;
}
Person(const Person &p){
cout << "Person的拷貝呼叫" << endl;
m_Age =p.m_Age;
}
~Person(){
cout << "person的解構式呼叫" << endl;
}
};
void test01(){
Person p1 = Person(20);
Person p2(p1);
}
void dowork(Person p){ }
void test02(){
Person p;
dowork(p);
}
Person dowork2(){
Person p1;
cout << (int*)&p1 << endl; //輸出地址
return p1;
}
void test03(){
Person p = dowork2();
cout << (int*)&p << endl;
}
int main(){
test01();
cout << endl;
test02();
cout << endl;
test03();
return 0;
}

建構式的呼叫規則
默認情況下,C++會至少給一個類添加3個函式
- 默認建構式無參,函式體為空
- 默認解構式無參,函式體為空
- 默認拷貝建構式,對屬性進行拷貝(所有的屬性都進行賦值操作)
規則如下:
- 如果用戶定義了有參建構式,C++不在提供默認無參構造,但會提供默認的拷貝
- 如果用戶定義了拷貝函式,C++不h會提供其他建構式
注意:若只定義了拷貝(只有參同理),則
Person p1;
//和
Person p1(20);
//均是錯誤的(因為系統不會提供)
深拷貝和淺拷貝
淺拷貝:簡單的賦值拷貝操作(如果對其進行釋放,則堆區的記憶體會重復釋放,出現錯誤)
深拷貝:在堆區重新申請空間,進行拷貝操作
m_height = new int(*P.m_height); //new回傳的值是地址
private:
int * m_height; //地址(指標)型別
初始化串列
作用:用來初始化屬性
語法:
建構式():屬性1(值1),屬性2(值2)...{}
來個淺例吧
#include <iostream>
using namespace std;
class Person{
public:
//傳統初始化
// Person(int a,int b,int c){
// m_A = a;
// m_B = b;
// m_C = c;
// }
//初始化串列
Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){} //引數a,b,c可以自由的改變所賦的值
int m_A;
int m_B;
int m_C;
};
void test01(){
Person p(30,20,10);
//Person p;
cout << "m_A:" << p.m_A << endl;
cout << "m_B:" << p.m_B << endl;
cout << "m_C:" << p.m_C << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果是30 20 10
類物件作為類的成員
C++類中的成員可以是另一個類的物件,我們成該成員為物件成員
例如:
class A{};
class B{
A a;
}
//敲黑板(好老的梗...)
//先構造A的物件(即先構造其他類的物件),再構造B的物件
//析構的順序是相反的,先析構本類,再析構其他類
代碼的簡單例子
#include <iostream>
using namespace std;
#include<cstring> //要用字串呢
class Phone{ //類一
public:
//品牌名字
string m_Pname;
Phone(string name){
m_Pname = name;
}
};
class Person{ //類二
public:
//姓名
string m_Name;
//手機
Phone m_Phone; //類一作為類二的成員
Person(string Name,string Pname):m_Name(Name),m_Phone(Pname){}
//相當于Phone m_Phone = Pname = Phone(Pname)(隱式轉化)
};
void test01(){
Person p("張三","華為");
cout << p.m_Phone.m_Pname;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果:華為
靜態成員函式
靜態成員變數就是加上const
靜態成員分成:
- 靜態成員變數
- 所有物件共享一份資料
- 再編譯階段分配記憶體
- 類內宣告,類外初始化
- 靜態成員函式
- 所有物件共享一個函式
- 靜態成員函式只能訪問靜態成員變數(函式體內無法區分普通變數是那個物件的成員)
- 也是有訪問權限的,private下在類外就訪問不到
#include <iostream>
using namespace std;
class Person{
public:
//靜態成員函式
void static func(){
m_a = 100;//靜態成員函式訪問靜態成員變數
cout << "func的呼叫" << m_a << endl;
}
static int m_a;//類內宣告類外初始化
};
int Person::m_a = 0;
//兩種訪問方式
void test01(){
//通過物件訪問
Person p;
p.func();
//通過類名訪問
Person::func();
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果

物件模型和this指標
成員變數和成員函式分開存盤
-
只有非靜態成員變數才屬于類的物件上面
-
空物件占用一個位元組
C++編譯器會給每個空物件分配一個位元組的空間(獨一無二的記憶體地址),防止區分空物件占記憶體的位置
#include <iostream>
using namespace std;
class Person1{};
class Person2{
int m_a;//非靜態成員變數,屬于類的物件上
static int m_b;//靜態成員變數,不屬于類的物件上
void test01(){}//非靜態成員函式,不屬于類的物件上
static void test02(){}//靜態成員函式,不屬于類的物件上
};
int Person2::m_b = 0;
void test01(){//空物件所占用的記憶體
Person1 p1;
cout << "p1 sizeof of p is " << sizeof(p1) << endl;
}
void test02(){//非空物件占用的記憶體
Person2 p2;
cout << "p2 sizeof of p is " << sizeof(p2) << endl;
}
int main(int argc, char** argv) {
test01();
test02();
return 0;
}

this指標
引子:在上面我們知道,非靜態的成員函式只會生成一份函式實體,也是是說多個同類的物件會公用一塊代碼(一個函式),那么:這一塊代碼是如何區分是那個物件呼叫自己呢?
通過this指標來解決上面的問題,this指標指向被呼叫的成員函式所屬的物件(eg:p1呼叫就指向p1...)
- this指標是隱含在每一個非靜態成員函式內的一種指標,不用定義,直接使用
用途
- 當形參和成員變數重名時,可用this來區分
- 在類的非靜態成員函式回傳物件本身(return *this;)
#include <iostream>
using namespace std;
class Person1{//名稱沖突
public:
Person1(int age,int age1){
this->age = age;
age1 = age1;
}
int age;
int age1;
Person1 & Add(Person1 &p){
this->age += p.age;
//this是一個指向p3的指標,*this就是物件p3的本體
return *this;
}
};
void test01(){
Person1 p1(18,18);
cout << "p1的年齡是" << p1.age << endl;
cout << "p1的年齡是" << p1.age1 << endl;
}
void test02(){//把p2的年齡加到p3上
Person1 p2(10,10);
Person1 p3(10,10);
p3.Add(p2).Add(p2);//鏈式編程思想
cout << "p3的年齡是" << p3.age << endl;
}
int main(int argc, char** argv) {
test01();
test02();
return 0;
}
結果

空指標訪問成員函式
C++中允許空指標呼叫成員函式的,但是也要注意有沒有用到this地址
如果用到this指標,則需要加以判斷確保代碼的健壯性
if(this == NULL) return;
看個小例子吧
#include <iostream>
using namespace std;
class Person{
public:
void show(){
cout << "show的呼叫" << endl;
}
int m_age;
void get(){
if(this == NULL) return;
cout << "age=" << m_age << endl;
//默認this->m_age
}
};
void test01(){
Person * p = NULL;
//空指標可以訪問成員
p->show();
p->get();
}
int main(int argc, char** argv) {
test01();
return 0;
}

const修飾成員函式
常函式
- 不可以修改成員屬性
- 成員屬性宣告時+mutable關鍵字,在常函式中就可以修改了
class Person{
public:
//this本質 指標常量 Person * const this 指向不可以改變
//在成員函式后面+const <=>const Person * const this,讓指標指向的值不可以改變
void show() const{
m_a =100;//所以會報錯哦
//其實是this->m_a = 100;
}
int m_a;
};

常物件
- 常物件只能呼叫常函式
const Person p;
p.show();
友元
在程式里,有些私有的屬性想讓類外的特殊的一些函式或者類進行呼叫,就需要友元技術
作用(目的):讓一個函式或是類訪問另一個類中的私有成員
友元關鍵字:friend(友元:不是類的成員,不受訪問限制)
友元的三種實作
- 全域函式友元
- 類做友元
- 成員函式做友元
全域函式做友元
#include <iostream>
using namespace std;
#include<cstring>
class Building{
//goodFriend是Building類的好朋友,可以訪問啦
friend void goodFriend(Building &building);
public:
Building(){
SittingRoom = "客廳";
BedRoom = "臥室";
}
public:
string SittingRoom;//客廳
private:
string BedRoom;//臥室
};
//全域函式
void goodFriend(Building &building){
cout << "友元全域函式 正在訪問:" << building.SittingRoom << endl;
cout << "友元全域函式 正在訪問:" << building.BedRoom << endl;
}
void test01(){
Building building;
goodFriend(building);
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果

類做友元
大致流程:
- 先創建GoodFriend類的物件GF
- 呼叫本類下的建構式:創建一個Building(同時呼叫Building的建構式)
- 訪問visit()函式,就可以訪問building下的成員啦
#include <iostream>
using namespace std;
#include<cstring>
class Building{
//GoodFriend類是Building類的好朋友
friend class GoodFriend;
...//和上面一樣
};
class GoodFriend{
public:
GoodFriend(){
//創建物件
building = new Building;
}
void visit(){//參觀函式 訪問Building中的屬性
cout << "友元正在訪問:" << building->SittingRoom << endl;
cout << "友元正在訪問:" << building->BedRoom << endl;
}
Building * building;
};
void test01(){
GoodFriend GF;
GF.visit();
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果

成員函式做友元
流程與上面的幾乎一樣
#include <iostream>
using namespace std;
#include<cstring>
class Building;//防止在未創建BUilding類是報錯
class GoodFriend{
public:
Building * building;
GoodFriend();
void visit();//參觀函式 訪問Building中的私有成員
};
class Building{
//visit()做為BUilding類的好朋友
friend void GoodFriend::visit();
public:
string SittingRoom;//客廳
private:
string BedRoom;//臥室
public:
Building();
};
//類外宣告
Building::Building(){
SittingRoom = "客廳";
BedRoom = "臥室";
}
GoodFriend::GoodFriend(){
building = new Building;
}
void GoodFriend::visit(){//參觀函式 訪問Building中的私有成員
cout << "友元正在訪問:" << building->SittingRoom << endl;
cout << "友元正在訪問:" << building->BedRoom << endl;
}
void test01(){ //測驗函式
GoodFriend GF;
GF.visit();
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果:

運算子的多載
概念:對已有運算子重新進行定義,賦予其另一種功能,以適應不同的資料型別
對于內置的資料型別,系統知道如何進行運算
加號運算子多載(其他同理)
- 成員函式多載+號
本質:Person p3 = p1.operator+(p2);
#include <iostream>
using namespace std;
class Person{
public:
int m_A;
int m_B;
/*======================================================*/
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;
}
/*======================================================*/
};
void test01(){
Person p1;
p1.m_A = 10;
p1.m_B = 10;
Person p2;
p2.m_A = 10;
p2.m_B = 10;
Person p3 = p1 + p2;
cout << p3.m_A <<endl;
cout << p3.m_B <<endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果是兩個20(相加成功)
- 全域函式多載+號
本質:Person p3 = operator+(p1,p2);
#include <iostream>
using namespace std;
class Person{
public:
int m_A;
int m_B;
};
/*======================================================*/
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;
}
/*======================================================*/
int main(int argc, char** argv) { //函式和上面的一樣
test01();
return 0;
}
結果也是兩個20
注意:- 運算子的多載也可以發生函式多載(名字相同,引數不同)
- 不可以改變內置運算子

左移運算子的多載(<<)
只能利用全域函式多載左移運算子
作用:輸出自定義的資料型別
本質:operator<<(cout,p) => cout << p
#include <iostream>
using namespace std;
class Person{
public:
int m_A;
int m_B;
};
/*======================================================*/
ostream & operator<<(ostream &out,Person &p){
out << "m_A:" << p.m_A << '\t' << "m_B:" << p.m_B << endl;
return out;
}
/*======================================================*/
void test01(){
Person p;
p.m_A = 10;
p.m_B = 10;
cout << p << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果:m_A:10 m_B:10
若類的成員變成私有:用友元
class Person{
friend ostream & operator<<(ostream &out,Person &p);
public:
int m_A;
int m_B;
};
遞增運算子多載(++)
前置回傳參考,后置回傳值
- 前置遞增
#include <iostream>
using namespace std;
class MyInteger{//自定義的整型
friend ostream & operator<<(ostream &out,MyInteger &p);
public:
MyInteger(){
m_Num = 0;
}
/*======================================================*/
MyInteger & operator++(){//回傳參考是為了一直對一個資料操作
m_Num++;
return *this;
}
/*======================================================*/
private:
int m_Num;
};
ostream & operator<<(ostream &out,MyInteger &p){
out << p.m_Num;
return out;
}
void test01(){
MyInteger myint;
cout << "myint:" << ++(++myint) << endl;
cout << "myint:" << myint << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果1:myint:2(換行)myint:2
- 后置遞增
#include <iostream>
using namespace std;
class MyInteger{//自定義的整型
friend ostream & operator<<(ostream &out,const MyInteger &p);
public:
MyInteger(){
m_Num = 0;
}
/*======================================================*/
MyInteger operator++(int){//int 代表占位引數,可以用于區分前置和后置遞增
//先記錄
MyInteger temp = *this;
//后遞增
m_Num++;
//再回傳
return temp;
}
/*======================================================*/
private:
int m_Num;
};
ostream & operator<<(ostream &out, const MyInteger &p){//這里加了const,否則在test02()的輸出會有問題
out << p.m_Num;
return out;
}
void test02(){
MyInteger myint;
cout << "myint:" << myint++ << endl;
cout << "myint:" << myint << endl;
}
int main(int argc, char** argv) {
test02();
return 0;
}
結果2:myint:0(換行)myint:1
賦值運算子多載
補充建構式呼叫規則,一個類至少4個函式
- 第四個:賦值運算子operator=,對屬性進行拷貝
p2 = p1的問題:堆區重復釋放,和淺拷貝的問題是一樣的
#include <iostream>
using namespace std;
class Person{
public:
int *m_age;//開辟到堆區
Person(int age){
m_age = new int(age);
}
~Person(){
if(m_age != NULL){
delete m_age;
m_age = NULL;
}
}
/*======================================================*/
Person & operator=(Person &p){//和深拷貝幾乎是一樣的,回傳值是參考是要滿足連等
//先判斷左值是否有屬性在堆區,如果有,先釋放干凈,再深拷貝
if(m_age != NULL){
delete m_age;
m_age = NULL;
}
m_age = new int(*p.m_age);
//回傳物件本身
return *this;
}
};
/*======================================================*/
void test01(){
Person p1(10);
Person p2(20);
Person p3(30);
p3 = p2 = p1;//賦值操作
cout << "p1的年齡是:" << *p1.m_age << endl;
cout << "p2的年齡是:" << *p2.m_age << endl;
cout << "p3的年齡是:" << *p3.m_age << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果:p1,p2,p3都是10
關系運算子的多載(>/<...)
- ==的多載(!=同理)
#include <iostream>
using namespace std;
class Person{
public:
string m_name;
int m_age;
Person(string name,int age){
m_name = name;
m_age = age;
}
~Person(){}
/*======================================================*/
bool operator==(Person &p){
if(this->m_name == p.m_name&&this->m_age == p.m_age){
return true;
}else{
return false;
}
}
/*======================================================*/
};
void test01(){
Person p1("Tom",18);
Person p2("Tom",18);
if(p1 == p2) cout << "p1和p2相等" << endl;
else cout << "p1和p2不相等" << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果:p1和p2相等
函式呼叫運算子多載
- 函式呼叫運算子()也可以多載
- 由于多載后使用的方式非常像函式的呼叫,因此也稱謂仿函式
- 仿函式沒有固定的寫法,很靈活
#include <iostream>//寫了兩個...
using namespace std;
#include<cstring>
class Myprint{//列印類
public:
/*======================================================*/
void operator()(string test){
cout << test <<endl;
}
/*======================================================*/
};
class MyAdd{//加法類
public:
/*======================================================*/
int operator()(int a,int b){
return a+b;
}
/*======================================================*/
};
void test01(){
Myprint myPrint;
myPrint("hello word");
MyAdd myAdd;
int c = myAdd(1,2);
cout << c << endl;
//匿名物件
cout << MyAdd()(1,1) << endl;
}
int main(int argc, char** argv) {
test01();
return 0;
}
結果:hello word(換行)3(換行)2
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/459533.html
標籤:其他
上一篇:python輸入和輸出
