#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
//如何讓定義一個類
// 封裝
// 1、將數據和方法定義到一起。
// 2、把想給你看的數據給你看,不想給你看的封裝起來。 ?通過訪問限定符來實現
class Stack
{
public:
?? ?//1.成員函數
?? ?void Push(int x);
?? ?//void Push(int x)
?? ?//{
?? ?//?? ?//...函數體?
?? ?//}//這種是在類里面定義
?? ?void Pop();//這里是類的申明,類外面定義
?? ?bool Empty();
?? ?//....
private:
?? ?//2.成員變量
?? ?int _a;
?? ?int _size;
?? ?int _capacity;//這里是聲明
};
//在類外面定義成員函數
void Stack::Push(int x)
{
?? ?//...
}
// 1、C語言中,struct是用來定義結構體的
// 2、C++中,兼容C的struct定義結構體的用法。但是同時struct也可以用來定義類
// 3、C++中使用class和struct定義類的區別? -》默認的訪問限定符
//C
struct ListNode_C
{
?? ?int* _val;
?? ?struct ListNode_C* _next;
?? ?struct ListNode_C* _prev;
};
//C++
struct ListNode_CPP//默認共有
//class ListNode_CPP//默認私有
{
?? ?int* _val;
?? ?//ListNode_CPP* _next;//這個地方不需要加struct
?? ?struct ListNode_CPP* _next;//這個地方也可以這樣用,C++兼容C
?? ?ListNode_CPP* _prev;//這是C++當成類的用法
?? ?//還可以定義成員函數
?? ?ListNode_CPP* Create(int val);
};
// 1、聲明和定義的區別? 聲明是一種承諾,承諾要干嘛,但是還沒做,定義就是把這個事落地了
//類中既有成員變量,又有成員函數
class A1
{
public:
?? ?void f1()
?? ?{
?? ?}
private:
?? ?int _a;
?? ?char _ch;
};
//類中僅有成員函數
class A2
{
public:
?? ?void f2()
?? ?{
?? ?}
};
//類中既沒有成員函數,也沒有成員變量
class A3
{
};
int main()
{
?? ?//類實例化出對象,相當于定義出了類的成員變量
?? ?Stack s1;
?? ?Stack s2;
?? ?Stack s3;
?? ?//s1._a = nullptr;//私有的,不能訪問
?? ?s1.Push(1);
?? ?//對象中只存儲成員變量,不存儲成員函數?為什么?
?? ?cout << sizeof(s1) << endl;
?? ?// 因為一個類實例化出N個對象,每個對象的成員變量都可以存儲不同的值,但是調用的函數確是同一個
?? ?// 如果每個對象都放成員函數,而這些成員函數卻是一樣的,會浪費空間
?? ?// 如何計算一個類實例化出的對象的大小,計算成員變量之和,并且考慮內存對齊規則
?? ?// 沒有成員變量的類的大小是1,面試題:為什么是1,而不是0。開1個字節不是為了存數據,而是占位
?? ?cout << sizeof(A1) << endl;
?? ?cout << sizeof(A2) << endl;
?? ?cout << sizeof(A3) << endl;
?? ?return 0;
}
實例化 -》就是用自己定義的類型定義出對象
?1、內置類型,基本類型 int/char/double
?2、自定義類型,class/struct
class Date
{
?? ?// this是誰調用這個成員函數,this就指向誰
public:
?? ?void Init(int year, int month, int day)//void Init(Date* this, int year, int month, int day)
?? ?{
?? ??? ?_year = year;
?? ??? ?_month = month;
?? ??? ?_day = day;
?? ?}
?? ?void Print()//void Print(Date* this)
?? ?{
?? ??? ?cout << _year << "-" << _month << "-" << _day << endl;
?? ?}
private:
?? ?int _year;
?? ?int _month;
?? ?int _day;
};
int main()
{
?? ?Date d1;
?? ?d1.Init(2025, 2, 20); //(&d1,2025,2,20);
?? ?d1.Print(); ? ? ? ? ? //(&d1);
?? ?return 0;
}
this指針存在哪里? (也就是存在進程地址空間的哪個區域?)
答:棧上的,因為它是一個形參。(ps:vs下是在ecx這個寄存器,來傳遞)
?1、下面程序能編譯通過嗎?
?2、下面程序會崩潰嗎?在哪里崩潰?
class A
{
public:
?? ?void PrintA()
?? ?{
?? ??? ?cout << _a << endl;
?? ??? ?//cout << this->_a << endl;//會對空指針this進行解引用,會導致程序崩潰
?? ?}
?? ?void Show()
?? ?{
?? ??? ?cout << "Show()" << endl;
?? ?}
private:
?? ?int _a;
};
int main()
{
?? ?A* p = nullptr; //因為p->PrintA();用的是指針,所以p->Print(p)傳的也是指針p
?? ?p->PrintA(); ?//崩潰
?? ?p->Show(); ? ?//正常運行
?? ?//成員函數存在公共的代碼段,所有p->Show()這里不會去p指向的對象上找
?? ?//訪問成員函數,才會去找
?? ?A a;
?? ?a.PrintA();//p->Print(&a);這個地方a是對象,所以傳的是&a,反正傳的都是對象的地址
?? ?return 0;
}
構造函數是特殊的成員函數,需要注意的是,構造函數雖然名稱叫構造,但是構造函數的主要任
務并不是開空間創建對象,而是初始化對象。
1. 函數名與類名相同。
2. 無返回值。
3. 對象實例化時編譯器自動調用對應的構造函數。
4. 構造函數可以重載。
class Date
{
public:
?? ?//構造函數-》在對象構造時調用的函數,這個函數完成初始化工作
?? ?Date(int year, int month, int day)//構造函數沒有返回值
?? ?{
?? ??? ?_year = year;
?? ??? ?_month = month;
?? ??? ?_day = day;
?? ?}
?? ?Date()//函數重載
?? ?{
?? ??? ?_year = 0;
?? ??? ?_month = 1;
?? ??? ?_day = 1;
?? ?}
?? ?void Print()
?? ?{
?? ??? ?cout << _year << "-" << _month << "-" << _day << endl;
?? ?}
private:
?? ?int _year;
?? ?int _month;
?? ?int _day;
};
int main()
{
?? ?//對象實例化時自動調用
?? ?Date d1(2025, 2, 20);
?? ?d1.Print();
?? ?Date d2;//Date d2();不能加括號,調無參的不需要加括號,否則編譯器不認識
?? ?d2.Print();
?? ?return 0;
}
5. 如果類中沒有顯式定義構造函數,則C++編譯器會自動生成一個無參的默認構造函數,一旦
用戶顯式定義編譯器將不再生成。
class Time
{
public:
?? ?Time()
?? ?{
?? ??? ?cout << "Time()" << endl;
?? ??? ?_hour = 0;
?? ??? ?_minute = 0;
?? ??? ?_second = 0;
?? ?}
private:
?? ?int _hour;
?? ?int _minute;
?? ?int _second;
};
class Date
{
public:
?? ?// 如果用戶顯式定義了構造函數,編譯器將不再生成
?? ?// 我們沒有顯式定義的構造函數,這里編譯器生成無參默認的構造函數(語法雙標)
?? ?// 1、針對內置類型的成員變量沒有做處理
?? ?// 2、針對自定義類型的成員變量,調用它的構造函數初始化
?? ?void Print()
?? ?{
?? ??? ?cout << _year << "-" << _month << "-" << _day << endl;
?? ?}
private:
? 基本類型(內置類型)
?? ?int _year;
?? ?int _month;
?? ?int _day;
? 自定義類型
?? ?Time _t;
};
int main()
{
?? ?Date d1;//調用編譯器生成的構造函數
?? ?d1.Print();
?? ?return 0;
}
7. 無參的構造函數和全缺省的構造函數都稱為默認構造函數,并且默認構造函數只能有一個。
注意:無參構造函數、全缺省構造函數、我們沒寫編譯器默認生成的構造函數,都可以認為是默認構造函數。
class Date
{
public:
?? ?//更好的方式:全缺省
?? ?Date(int year = 0, int month = 1, int day = 1)
?? ?{
?? ??? ?_year = year;
?? ??? ?_month = month;
?? ??? ?_day = day;
?? ?}
?? ?//不能和全缺省同時出現,因為調用的時候會出現歧義,Date d1;
?? ?//Date()//函數重載
?? ?//{
?? ?//?? ?_year = 0;
?? ?//?? ?_month = 1;
?? ?//?? ?_day = 1;
?? ?//}
?? ?void Print()
?? ?{
?? ??? ?cout << _year << "-" << _month << "-" << _day << endl;
?? ?}
private:
?? ?int _year;
?? ?int _month;
?? ?int _day;
};
int main()
{
?? ?Date d1;//調用默認構造函數 -》1、自己實現無參的構造函數 ?2、自己實現的全缺省構造函數?
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//3、編譯器自動實現的構造函數 Date d1;的特點是不用傳參數?
?? ?d1.Print();
?? ?Date d1(2025, 3, 20);
?? ?d1.Print();
?? ?return 0;
}
class Date
{
public:
?? ?更好的方式:全缺省
?? ?Date(int year = 0, int month = 1, int day = 1)
?? ?{
?? ??? ?_year = year;
?? ??? ?_month = month;
?? ??? ?_day = day;
?? ?}
?? ?不能和全缺省同時出現,因為調用的時候會出現歧義,Date d1;
?? ?Date()//函數重載
?? ?{
?? ??? ?_year = 0;
?? ??? ?_month = 1;
?? ??? ?_day = 1;
?? ?}
?? ?void Print()
?? ?{
?? ??? ?cout << _year << "-" << _month << "-" << _day << endl;
?? ?}
private:
?? ?int _year;
?? ?int _month;
?? ?int _day;
};
int main()
{
?? ?Date d1;
?? ?Date d2;
?? ?return 0;
}