C++类和对象(二)

༺ 个人主页 · 纪念229 ༻

🏠我的博客主页🏠

༒专栏目录:《数据结构》༒

༒专栏目录:《算法》༒

༒专栏目录:《C++》༒

༒专栏目录:《MySQL数据库》༒

༒专栏目录:《前端开发》༒

༒其它有趣的计算机知识༒

༺世上本没有路,走的人多了自然就有了༻


本文讲述的是C++中类和对象的内容,希望对你有所帮助

介绍完后我将列出一些代码进行讲解


文章目录

  • [1. 类的默认成员函数](#1. 类的默认成员函数)
  • [2. 构造函数](#2. 构造函数)
  • [3. 析构函数](#3. 析构函数)
  • 4.类和对象代码综合讲解

1. 类的默认成员函数

默认成员函数就是⽤⼾没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个类,我们不写的情况下编译器会默认⽣成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最

后两个取地址重载不重要,我们稍微了解⼀下即可。其次就是C++11以后还会增加两个默认成员函数,移动构造和移动赋值,这个我们后⾯再讲解。默认成员函数很重要,也⽐较复杂,我们要从两个⽅⾯

去学习:

• 第⼀:我们不写时,编译器默认⽣成的函数⾏为是什么,是否满⾜我们的需求。

• 第⼆:编译器默认⽣成的函数不满⾜我们的需求,我们需要⾃⼰实现,那么如何⾃⼰实现?

2. 构造函数

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象(我们常使⽤的局部对象是栈帧创建时,空间就开好了),⽽是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能,构造函数⾃动调⽤的特点就完美的替代的了Init。

构造函数的特点:

  1. 函数名与类名相同。
  2. ⽆返回值。 (返回值啥都不需要给,也不需要写void,不要纠结C++规定如此)
  3. 对象实例化时系统会⾃动调⽤对应的构造函数。
  4. 构造函数可以重载。
  5. 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦⽤⼾显式定义编译器将不再⽣成。
  6. ⽆参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函数。但是这三个函数有且只有⼀个存在,不能同时存在。⽆参构造函数和全缺省构造函数虽然构成
    函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结⼀下就是不传实参就可以调⽤的构造就叫默认构造。
  7. 我们不写,编译器默认⽣成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始化是不确定的,看编译器。对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要⽤初始化列表才能解决,初始化列表,我们下个章节再细细讲解。

说明:C++把类型分成内置类型(基本类型)和⾃定义类型。内置类型就是语⾔提供的原⽣数据类型,如:int/char/double/指针等,⾃定义类型就是我们使⽤class/struct等关键字⾃⼰定义的类型。

代码展示

c 复制代码
#include<iostream>
using namespace std;
class Date
{ p
ublic:
// 1.⽆参构造函数
Date()
{
_year = 1;
_month = 1;
_day = 1;
} /
/ 2.带参构造函数
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
} /
/ 3.全缺省构造函数
/*Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}*/
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
// 如果留下三个构造中的第⼆个带参构造,第⼀个和第三个注释掉
// 编译报错:error C2512: "Date": 没有合适的默认构造函数可⽤
Date d1; // 调⽤默认构造函数
Date d2(2025, 1, 1); // 调⽤带参的构造函数
// 注意:如果通过⽆参构造函数创建对象时,对象后⾯不⽤跟括号,否则编译器⽆法
// 区分这⾥是函数声明还是实例化对象
// warning C4930: "Date d3(void)": 未调⽤原型函数(是否是有意⽤变量定义的?)
Date d3();
d1.Print();
d2.Print();
return 0;
}
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{ p
ublic:
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
} _
capacity = n;
_top = 0;
} /
/ ...
private:
STDataType* _a;
size_t _capacity;
size_t _top;
};
// 两个Stack实现队列
class MyQueue
{ p
ublic:
//编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化
private:
Stack pushst;
Stack popst;
};
int main()
{
MyQueue mq;
return 0;
}

构造函数就是帮助对象类变量初始化的

3. 析构函数

析构函数与构造函数功能相反,析构函数不是完成对对象本⾝的销毁,⽐如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会⾃动调⽤析构函数,完成对

象中资源的清理释放⼯作。析构函数的功能类⽐我们之前Stack实现的Destroy功能,⽽像Date没有Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的。

析构函数的特点:

  1. 析构函数名是在类名前加上字符 ~。

  2. ⽆参数⽆返回值。 (这⾥跟构造类似,也不需要加void)

  3. ⼀个类只能有⼀个析构函数。若未显式定义,系统会⾃动⽣成默认的析构函数。

  4. 对象⽣命周期结束时,系统会⾃动调⽤析构函数。

  5. 跟构造函数类似,我们不写编译器⾃动⽣成的析构函数对内置类型成员不做处理,⾃定类型成员会调⽤他的析构函数。

  6. 还需要注意的是我们显⽰写析构函数,对于⾃定义类型成员也会调⽤他的析构,也就是说⾃定义类型成员⽆论什么情况都会⾃动调⽤析构函数。

  7. 如果类中没有申请资源时,析构函数可以不写,直接使⽤编译器⽣成的默认析构函数,如Date;如果默认⽣成的析构就可以⽤,也就不需要显⽰写析构,如MyQueue;但是有资源申请时,⼀定要⾃⼰写析构,否则会造成资源泄漏,如Stack。

  8. ⼀个局部域的多个对象,C++规定后定义的先析构。

代码展示

c 复制代码
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{ p
ublic:
Stack(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申请空间失败");
return;
} _
capacity = n;
_top = 0;
} ~
Stack()
{
cout << "~Stack()" << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
STDataType* _a;
size_t _capacity;
size_t _top;
};
// 两个Stack实现队列
class MyQueue
{ p
ublic:
//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
// 显⽰写析构,也会⾃动调⽤Stack的析构
/*~MyQueue()
{}*/
private:
Stack pushst;
Stack popst;
};
int main()
{
Stack st;
MyQueue mq;
return 0;
}

对⽐⼀下⽤C++和C实现的Stack解决之前括号匹配问题isValid,我们发现有了构造函数和析构函数确实⽅便了很多,不会再忘记调⽤Init和Destory函数了,也⽅便了不少。

代码展示

c 复制代码
#include<iostream>
using namespace std;
// ⽤最新加了构造和析构的C++版本Stack实现
bool isValid(const char* s) {
Stack st;
while (*s)
{
if (*s == '[' || *s == '(' || *s == '{')
{
st.Push(*s);
} e
lse
{
// 右括号⽐左括号多,数量匹配问题
if (st.Empty())
{
return false;
} /
/ 栈⾥⾯取左括号
char top = st.Top();
st.Pop();
// 顺序不匹配
if ((*s == ']' && top != '[')
|| (*s == '}' && top != '{')
|| (*s == ')' && top != '('))
{
return false;
}
} +
+s;
} /
/ 栈为空,返回真,说明数量都匹配 左括号多,右括号少匹配问题
return st.Empty();
} /
/ ⽤之前C版本Stack实现
bool isValid(const char* s) {
ST st;
STInit(&st);
while (*s)
{
// 左括号⼊栈
if (*s == '(' || *s == '[' || *s == '{')
{
STPush(&st, *s);
} e
lse // 右括号取栈顶左括号尝试匹配
{
if (STEmpty(&st))
{
STDestroy(&st);
return false;
} c
har top = STTop(&st);
STPop(&st);
// 不匹配
if ((top == '(' && *s != ')')
|| (top == '{' && *s != '}')
|| (top == '[' && *s != ']'))
{
STDestroy(&st);
return false;
}
} +
+s;
} /
/ 栈不为空,说明左括号⽐右括号多,数量不匹配
bool ret = STEmpty(&st);
STDestroy(&st);
return ret;
} i
nt main()
{
cout << isValid("[()][]") << endl;
cout << isValid("[(])[]") << endl;
return 0;
}

析构函数作用是将对象中类变量中的开过空间的值返还最后自动调用

本节类和对象的基本介绍就到这里接下来我们来看看类和对象代码的综合讲解

4.类和对象代码综合讲解

之前解释过的代码就不过多赘述了
代码有点多,可以从后面代码讲解开始

代码展示

c 复制代码
//#define _CRT_SECURE_NO_WARNINGS 1
//
//#include<iostream>
//#include<cassert>
//using namespace std;
//
////class Date
////{
////public:
////	void Init(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////
////	void Print()
////	{
////		cout << _year << "/" << _month << "/" << _day << endl;
////	}
//////private:
////	int _year; //声明
////	int _month;
////	int _day;
////};
////
////class A
////{
////public:
////	void Print()
////	{
////		cout << _ch << endl;
////	}
////private:
////	char _ch;
////	int _i;
////};
////
////class B
////{
////public:
////	void Print()
////	{
////		//...
////	}
////
////};
////
////class C
////{ };
////
////int main()
////{
//////类的字节数遵循对齐原则,看的是类的内容(private里的东西)
//////不同类名字节数不同
//////sizeof不需要任何头文件	
////	cout << sizeof(A) << endl;
////	cout << sizeof(B) << endl;
////	cout << sizeof(C) << endl;
////
////	//没有成员变量的对象, 分配1byte纯粹为占位,表示对象存在
////
////	B b1;
////	B b2;
////
////	cout << &b1 << endl;
////	cout << &b2 << endl;
////
////	Date d1;//类实列化对象
////	Date d2;
////
////	d1.Init(2024, 12, 1);
////	d2.Init(2025, 11, 2);
////
////	//这两个_year需要不同空间储存独立的值
////	d1._year++;
////	d2._year++;
////
////	//Print函数指针是一样的,不需要单独的空间
////	d1.Print();
////	d2.Print();
////
////	cout << sizeof(Date) << endl;
////	cout << sizeof(d1) << endl;
////	return 0;
////}
//
////class Date
////{
////public:
////	void Init(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////
////	}
////	void Print()
////	{
////		cout << this << endl;
////		//this = nullptr; this本身不能修改
////        //形参this指针指的是该类对象的地址(this指针在类函数形参里)
////		//因为作为形参所以this在栈里
////		cout << this->_year << "/" << this->_month << "/" << _day << endl;
////	}
//
//	/*void Print(Date* const this)
//	{
//		cout << this->_year << "/" << this->_month << "/"
//			<< this->_day << endl;
//	}*/
//
////private:
////	int _year;
////	int _month;
////	int _day;
////};
////
////
////
////
////
////
////
////int main()
////{
////	Date d1;
////	Date d2;
////
////	cout << &d1 << endl;
////	cout << &d2 << endl << endl;
////
////	d1.Init(2024, 12, 1);
////	d2.Init(2025, 11, 2);
////
////	d1.Print();
////	d2.Print();
////
////	/*d1.Print(&d1);
////	d1.Print(&d2);*/
////
////	//main函数最后记得加return 0;
////	return 0;
////}
////class A
////{
////public:
////	void Print()
////	{
////		cout << this << endl;
////		cout << "A::Print()" << endl;
////		cout << _a << endl;
////		//打印出较大值一般说明没初始化
////	}
////private:
////	int _a;
////};
////
////
////
////int main()
////{
////	A aa;
////	A* p = &aa;
////	p->Print();// call Print地址
////
////	return 0;
////}
//
////class A
////{
////public:
////	void Print()
////	{
////		cout << this << endl;
////		cout << "A::Print" << endl;
////	}
////private:
////	int a;
////};
////
////
////
////int main()
////{
////	A* p = nullptr;
////	(*p).Print();
////	//p->Print();
////	//成员函数不在对象内存中nullptr可以访问
////	//成员变量在对象内存nullptr访问崩溃
////	return 0;
////}
//
////class Stack
////{
////public:
////	void Init(int n = 4)
////	{
////		a = (int*)malloc(sizeof(int) * n);
////		top = capacity = 0;
////	 }
////	void Push(int x = 1)
////	{
////
////		//..扩增代码
////		a[top++] = x;
////	}
////	void Pop()
////	{
////		assert(top != 0);
////		top--;
////	}
////	int Top()
////	{
////		assert(top != 0);
////		return a[top - 1];
////	}
////private:
////	int* a;
////	int top;
////	int capacity;
//// };
//
////比较c语言写栈与c++写栈
////int main()
////{
////	Stack st1;
//// st1.Init();
//// st1.Push(1);
//// st1.Push(2);
//// st1.Push(3);
//// 
//// cout << st1.Top() << endl;
////st1.Pop();
////
////ST s;
//// STInit(&s);
//// STPush(&s, 1);
//// STPush(&s, 2);
//// STPush(&s, 3);
//// STPush(&s, 4);
////
////cout << STTOP(&s) << endl;
//// STPop(&s);
////cout << s.a[a.top - 1] << endl;
//// --s.top
////}
////
////有此可见c++在函数定义和使用等方面优于C语言
////以前学C语言过的是什么苦日子
////但是学了C语言后了解C++有很大好处C++入门较难
//
////构造函数
////class Date
////{
////public:
////	/*void Init(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}*/
////	//以下两个函数就是构造函数
////	//构造函数实际上就是帮对象中的类变量初始化
////	Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////	void Print()
////	{
////		cout << this->_year << "/" << this->_month << "/"
////			<< _day << endl;
////	}
////private:
////	int _year;
////	int _month;
////	int _day;
////};
////
////int main()
////{
////	//函数声明
////	//Date func();
////
////	//对象实例化一定会调用对应的构造,保证了对象实例化
////	//出来一定被初始化了
////	Date d1;//这么写就是调用第一个构造函数帮助对象初始化
////	d1.Print();
////
////	Date d2(2025, 4, 22);//这么写就是调用第二个构造函数帮助对象初始化
////	d2.Print();
////
////
////	return 0;
////}
//
//
////在类函数中类名 + (形参....)
////{
////  XXXXX
////}
////就是构造函数
////class Date
////{
////public:
////	Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////	void Print()
////	{
////		cout << this->_year << "/" << this->_month
////			<< "/" << this->_day << endl;
////	}
////private:
////	int _year;
////	int _month;
////	int _day;
////};
////
////
//////Date(类名) + 对象名就叫对象实例化
////
////int main()
////{
////	//对象实例化一定会调用对应的构造,保证了对象实例化
////	//出来一定被初始化了
////	Date d1;
////	d1.Print();
////
////	Date d2(2025, 4, 22);
////	d2.Print();
////
////	
////	return 0;
////}
//
////class Date
////{
////public:
////	//不写构造, 编译器生成默认构造(里面什么都没有)
////	Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////
////	void Print()
////	{
////		cout << this->_year << "/" << this->_month
////			<< "/" << _day << endl;
////	}
////private:
////	//内置类型
////	int _year;
////	int _month;
////	int _day;
////};
////
////typedef int STDataType;
////class Stack
////{
////public:
////	Stack(int n = 4)
////	{
////		cout << "Stack(int n = 4)" << endl;
////		_a = (STDataType*)malloc(sizeof(STDataType) * n);
////		if (nullptr == _a)
////		{
////			perror("malloc申请空间失败");
////			return;
////		}
////		//构造函数没有返回类型,但可以单独return;结束函数
////		_capacity = n;
////		_top = 0;
////	}
////
////	//析构函数
////	//构成
////	//~类名 + ()
////	//{
////	//free(类变量);
////	//........
////	//}
////	//析构函数自动调用
////	//构造函数类的实例化调用
////	~Stack()
////	{
////		cout << "~Stack()" << endl;
////		free(_a);
////		_a = nullptr;
////		_capacity = _top = 0;
////	}
////private:
////	STDataType* _a;
////	int _top;
////	int _capacity;
////};
////
//////两个栈实现一个队列
////class Myqueue
////{
////private:
////	Stack _psuhst;
////	Stack _popst;
////};
////
////
////
////
////
////
////int main()
////{
////	Date d1;
////	d1.Print();
////
////	/*Date d2(2025);
////	d2.Print();*/
////
////	/*Date d3(2025, 12);
////	d3.Print();*/
////
////	Date d4(2025, 12, 23);
////	d4.Print();
////
////	Myqueue q;
////
////	Stack st;
////
////	return 0;
////}
//
//
//typedef char STDataType;
//
//typedef struct Stack
//{
//	STDataType* a;
//	int top;
//	int capacity;
//}ST;
//
//
////初始化和销毁
//void STInit(ST* pst);
//void STPop(ST* pst);
//
////入栈 出栈 
//void STPush(ST* pst, STDataType x);
//void STDestroy(ST* pst);
//
////取栈顶数据
//STDataType STTop(ST* pst);
//
////判空
//bool STEmpty(ST* pst);
////获取数据个数
//int STSize(ST* pst);
//
////
////初始化和销毁
//void STInit(ST* pst)
//{
//	assert(pst);
//
//	pst->a = NULL;
//	//top指向栈数据的下一个位置
//	pst->top = 0;
//
//	//top指向栈顶数据
//	//pst->top = -1;
//
//	pst->capacity = 0;
//}
//
//void STDestroy(ST* pst)
//{
//	assert(pst);
//
//	free(pst->a);
//	pst->a = NULL;
//	pst->top = pst->capacity = 0;
//}
//
////入栈 出栈
//void STPush(ST* pst, STDataType x)
//{
//	assert(pst);
//	
//	//扩容
//	if (pst->capacity == pst->top)
//	{
//		int newcapacity = pst->top == 0 ? 4 : pst->capacity * 2;
//		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
//		if (tmp == NULL)
//		{
//			perror("realloc fail");
//			return;
//		}
//		pst->a = tmp;
//		pst->capacity = newcapacity;
//	}
//	pst->a[pst->top] = x;
//	pst->top++;
//}
//
//void STPop(ST* pst)
//{
//	assert(pst);
//	assert(pst->top > 0);
//	pst->top--;
//
//}
//
////取栈顶数据
//STDataType STTop(ST* pst)
//{
//	assert(pst);
//	assert(pst->top > 0);
//
//	return pst->a[pst->top - 1];
//}
//
////判空
////这里讲一下为什么可以用bool类型
////在VS里面头文件#include<iostream>里嵌套了
////头文件#include<cstdio>、#include<cstdlib>、#include<cstdbool>
////所以用assert要头文件#include<cassert>
//
//bool STEmpty(ST* pst)
//{
//	assert(pst);
//
//	return pst->top == 0;
//}
//
////获取数据个数
//int STSize(ST* pst)
//{
//	assert(pst);
//	return pst->top;
//}
//
//
//
//bool isValid(const char* s) {
//	ST st;
//	STInit(&st);
//	while (*s)
//	{
//		//左括号入栈
//		if (*s == '(' || *s == '[' || *s == '{'  )
//		{
//			STPush(&st, *s);
//		}
//		else//右括号取栈顶左括号尝试匹配
//		{
//			if (STEmpty(&st))
//			{
//				STDestroy(&st);
//				return false;
//			}
//
//			char top = STTop(&st);
//			STPop(&st);
//
//			//不匹配
//			if (top == '(' && *s != ')' ||
//				top == '[' && *s != ']' ||
//				top == '{' && *s != '}' 
//				)
//			{
//				STDestroy(&st);
//				return false;
//			}
//
//		}
//		s++;
//	}
//	//栈不为空说明左括号比右括号多,数量不匹配
//	bool ret = STEmpty(&st);
//	STDestroy(&st);
//
//	return ret;
//}
//
//
//
c 复制代码
//#include<iostream>
//#include<cassert>

这里讲一下#include里嵌套着#include、#include、#include

头文件#include没有头文件嵌套需要单独写

c 复制代码
////class A
////{
////public:
////	void Print()
////	{
////		cout << _ch << endl;
////	}
////private:
////	char _ch;
////	int _i;
////};
////
////class B
////{
////public:
////	void Print()
////	{
////		//...
////	}
////
////};
////
////class C
////{ };
////
////int main()
////{
//////类的字节数遵循对齐原则,看的是类的内容(private里的东西)
//////不同类名字节数不同
//////sizeof不需要任何头文件	
////	cout << sizeof(A) << endl;
////	cout << sizeof(B) << endl;
////	cout << sizeof(C) << endl;
////
////	//没有成员变量的对象, 分配1byte纯粹为占位,表示对象存在
////
////	B b1;
////	B b2;
////
////	cout << &b1 << endl;
////	cout << &b2 << endl;
////
////	Date d1;//类实列化对象
////	Date d2;
////
////	d1.Init(2024, 12, 1);
////	d2.Init(2025, 11, 2);
////
////	//这两个_year需要不同空间储存独立的值
////	d1._year++;
////	d2._year++;
////
////	//Print函数指针是一样的,不需要单独的空间
////	d1.Print();
////	d2.Print();
////
////	cout << sizeof(Date) << endl;
////	cout << sizeof(d1) << endl;
////	return 0;
////}

具体讲解

这里主要讲解的是不同类的字节个数不同且遵循对齐原理,

并且我们计类的字节计的是类变量的字节不计类函数的字节,与它们在访问限定符public和private无关

还有就是若是类的变量在类中没定义就默认占一个字节

代码展示

c 复制代码
////class Date
////{
////public:
////	void Init(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////
////	}
////	void Print()
////	{
////		cout << this << endl;
////		//this = nullptr; this本身不能修改
////        //形参this指针指的是该类对象的地址(this指针在类函数形参里)
////		//因为作为形参所以this在栈里
////		cout << this->_year << "/" << this->_month << "/" << _day << endl;
////	}
//
//	/*void Print(Date* const this)
//	{
//		cout << this->_year << "/" << this->_month << "/"
//			<< this->_day << endl;
//	}*/
//
////private:
////	int _year;
////	int _month;
////	int _day;
////};
////
////
////
////
////
////
////
////int main()
////{
////	Date d1;
////	Date d2;
////
////	cout << &d1 << endl;
////	cout << &d2 << endl << endl;
////
////	d1.Init(2024, 12, 1);
////	d2.Init(2025, 11, 2);
////
////	d1.Print();
////	d2.Print();
////
////	/*d1.Print(&d1);
////	d1.Print(&d2);*/
////
////	//main函数最后记得加return 0;
////	return 0;
////}

这里讲的就是this指针作为形参在类函数中,this指针指的就是对象的地址可以在类函数中使用

this指针在栈中因为this指针在类函数中是形参

c 复制代码
////class A
////{
////public:
////	void Print()
////	{
////		cout << this << endl;
////		cout << "A::Print()" << endl;
////		cout << _a << endl;
////		//打印出较大值一般说明没初始化
////	}
////private:
////	int _a;
////};
////
////
////
////int main()
////{
////	A aa;
////	A* p = &aa;
////	p->Print();// call Print地址
////
////	return 0;
////}
//

这里编译运行会得到随机数一般是大值

所以在编译结果看到较大的数时我们就要想到越界

c 复制代码
////class A
////{
////public:
////	void Print()
////	{
////		cout << this << endl;
////		cout << "A::Print" << endl;
////	}
////private:
////	int a;
////};
////
////
////
////int main()
////{
////	A* p = nullptr;
////	(*p).Print();
////	//p->Print();
////	//成员函数不在对象内存中nullptr可以访问
////	//成员变量在对象内存nullptr访问崩溃
////	return 0;
////}

这里讲述的是nullptr访问类中类函数

这里我们会有疑问不是说空指针访问会崩溃吗?

类函数不在对象内存访问不会崩溃,类变量在对象内存空指针访问崩溃

c 复制代码
///class Stack
////{
////public:
////	void Init(int n = 4)
////	{
////		a = (int*)malloc(sizeof(int) * n);
////		top = capacity = 0;
////	 }
////	void Push(int x = 1)
////	{
////
////		//..扩增代码
////		a[top++] = x;
////	}
////	void Pop()
////	{
////		assert(top != 0);
////		top--;
////	}
////	int Top()
////	{
////		assert(top != 0);
////		return a[top - 1];
////	}
////private:
////	int* a;
////	int top;
////	int capacity;
//// };

这是C++写栈的方式

c 复制代码
/typedef char STDataType;
//
//typedef struct Stack
//{
//	STDataType* a;
//	int top;
//	int capacity;
//}ST;
//
//
////初始化和销毁
//void STInit(ST* pst);
//void STPop(ST* pst);
//
////入栈 出栈 
//void STPush(ST* pst, STDataType x);
//void STDestroy(ST* pst);
//
////取栈顶数据
//STDataType STTop(ST* pst);
//
////判空
//bool STEmpty(ST* pst);
////获取数据个数
//int STSize(ST* pst);
//
////
////初始化和销毁
//void STInit(ST* pst)
//{
//	assert(pst);
//
//	pst->a = NULL;
//	//top指向栈数据的下一个位置
//	pst->top = 0;
//
//	//top指向栈顶数据
//	//pst->top = -1;
//
//	pst->capacity = 0;
//}
//
//void STDestroy(ST* pst)
//{
//	assert(pst);
//
//	free(pst->a);
//	pst->a = NULL;
//	pst->top = pst->capacity = 0;
//}
//
////入栈 出栈
//void STPush(ST* pst, STDataType x)
//{
//	assert(pst);
//	
//	//扩容
//	if (pst->capacity == pst->top)
//	{
//		int newcapacity = pst->top == 0 ? 4 : pst->capacity * 2;
//		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
//		if (tmp == NULL)
//		{
//			perror("realloc fail");
//			return;
//		}
//		pst->a = tmp;
//		pst->capacity = newcapacity;
//	}
//	pst->a[pst->top] = x;
//	pst->top++;
//}
//
//void STPop(ST* pst)
//{
//	assert(pst);
//	assert(pst->top > 0);
//	pst->top--;
//
//}
//
////取栈顶数据
//STDataType STTop(ST* pst)
//{
//	assert(pst);
//	assert(pst->top > 0);
//
//	return pst->a[pst->top - 1];
//}
//
////判空
////这里讲一下为什么可以用bool类型
////在VS里面头文件#include<iostream>里嵌套了
////头文件#include<cstdio>、#include<cstdlib>、#include<cstdbool>
////所以用assert要头文件#include<cassert>
//
//bool STEmpty(ST* pst)
//{
//	assert(pst);
//
//	return pst->top == 0;
//}
//
////获取数据个数
//int STSize(ST* pst)
//{
//	assert(pst);
//	return pst->top;
//}
//
//
//
//bool isValid(const char* s) {
//	ST st;
//	STInit(&st);
//	while (*s)
//	{
//		//左括号入栈
//		if (*s == '(' || *s == '[' || *s == '{'  )
//		{
//			STPush(&st, *s);
//		}
//		else//右括号取栈顶左括号尝试匹配
//		{
//			if (STEmpty(&st))
//			{
//				STDestroy(&st);
//				return false;
//			}
//
//			char top = STTop(&st);
//			STPop(&st);
//
//			//不匹配
//			if (top == '(' && *s != ')' ||
//				top == '[' && *s != ']' ||
//				top == '{' && *s != '}' 
//				)
//			{
//				STDestroy(&st);
//				return false;
//			}
//
//		}
//		s++;
//	}
//	//栈不为空说明左括号比右括号多,数量不匹配
//	bool ret = STEmpty(&st);
//	STDestroy(&st);
//
//	return ret;
//}
//

这是C语言写栈的方式

c 复制代码
///比较c语言写栈与c++写栈
////int main()
////{
////	Stack st1;
//// st1.Init();
//// st1.Push(1);
//// st1.Push(2);
//// st1.Push(3);
//// 
//// cout << st1.Top() << endl;
////st1.Pop();
////
////ST s;
//// STInit(&s);
//// STPush(&s, 1);
//// STPush(&s, 2);
//// STPush(&s, 3);
//// STPush(&s, 4);
////cout << STTOP(&s) << endl;
//// STPop(&s);
////cout << s.a[a.top - 1] << endl;
//// --s.top
////}

这是C语言和c++调用的方式

由此可见C++比c语言方便的多在大多数情况

接下来解释构造函数

c 复制代码
////构造函数
////class Date
////{
////public:
////	/*void Init(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}*/
////	//以下两个函数就是构造函数
////	//构造函数实际上就是帮对象中的类变量初始化
////	Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////	void Print()
////	{
////		cout << this->_year << "/" << this->_month << "/"
////			<< _day << endl;
////	}
////private:
////	int _year;
////	int _month;
////	int _day;
////};
////
////int main()
////{
////	//函数声明
////	//Date func();
////
////	//对象实例化一定会调用对应的构造,保证了对象实例化
////	//出来一定被初始化了
////	Date d1;//这么写就是调用第一个构造函数帮助对象初始化
////	d1.Print();
////
////	Date d2(2025, 4, 22);//这么写就是调用第二个构造函数帮助对象初始化
////	d2.Print();
////
////
////	return 0;
////}

构造函数作用就是初始化对象中的类的变量

可以用缺省参数一般写在访问限制符public里

结构:

类名 (形参(可有可无))

{}

调用:

类名 对象名;//这种构造函数没参数

类名 对象名(实参,...);

因为是类函数所以可以调用private里的类变量进行初始化

它和类函数里的自定义函数Init区别就是构造函数只要类实例化就会调用

解释一下什么是类的实例化

类名 对象名 //实例化

例子

Date d1;//这就是类的实例化

c 复制代码
Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}

这是我在public写的两种构造函数(函数重载)

我们也可以用缺省参数直接

Date d1;

d1的值你就可以决定

c 复制代码
///在类函数中类名 + (形参....)
////{
////  XXXXX
////}
////就是构造函数
////class Date
////{
////public:
////	Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////	void Print()
////	{
////		cout << this->_year << "/" << this->_month
////			<< "/" << this->_day << endl;
////	}
////private:
////	int _year;
////	int _month;
////	int _day;
////};
////
////
//////Date(类名) + 对象名就叫对象实例化
////
////int main()
////{
////	//对象实例化一定会调用对应的构造,保证了对象实例化
////	//出来一定被初始化了
////	Date d1;
////	d1.Print();
////
////	Date d2(2025, 4, 22);
////	d2.Print();
////
////	
////	return 0;
////}

这些也是构造函数将对象初始化的方法

c 复制代码
///class Date
////{
////public:
////	//不写构造, 编译器生成默认构造(里面什么都没有)
////	Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////
////	void Print()
////	{
////		cout << this->_year << "/" << this->_month
////			<< "/" << _day << endl;
////	}
////private:
////	//内置类型
////	int _year;
////	int _month;
////	int _day;
////};
////
////typedef int STDataType;
////class Stack
////{
////public:
////	Stack(int n = 4)
////	{
////		cout << "Stack(int n = 4)" << endl;
////		_a = (STDataType*)malloc(sizeof(STDataType) * n);
////		if (nullptr == _a)
////		{
////			perror("malloc申请空间失败");
////			return;
////		}
////		//构造函数没有返回类型,但可以单独return;结束函数
////		_capacity = n;
////		_top = 0;
////	}
////
////	//析构函数
////	//构成
////	//~类名 + ()
////	//{
////	//free(类变量);
////	//........
////	//}
////	//析构函数自动调用
////	//构造函数类的实例化调用
////	~Stack()
////	{
////		cout << "~Stack()" << endl;
////		free(_a);
////		_a = nullptr;
////		_capacity = _top = 0;
////	}
////private:
////	STDataType* _a;
////	int _top;
////	int _capacity;
////};
////
//////两个栈实现一个队列
////class Myqueue
////{
////private:
////	Stack _psuhst;
////	Stack _popst;
////};
////
////
////
////
////
////
////int main()
////{
////	Date d1;
////	d1.Print();
////
////	/*Date d2(2025);
////	d2.Print();*/
////
////	/*Date d3(2025, 12);
////	d3.Print();*/
////
////	Date d4(2025, 12, 23);
////	d4.Print();
////
////	Myqueue q;
////
////	Stack st;
////
////	return 0;
////}
c 复制代码
////class Date
////{
////public:
////	//不写构造, 编译器生成默认构造(里面什么都没有)
////	Date()
////	{
////		_year = 1;
////		_month = 1;
////		_day = 1;
////	}
////	Date(int year, int month, int day)
////	{
////		_year = year;
////		_month = month;
////		_day = day;
////	}
////
////	void Print()
////	{
////		cout << this->_year << "/" << this->_month
////			<< "/" << _day << endl;
////	}
////private:
////	//内置类型
////	int _year;
////	int _month;
////	int _day;
////};

这里想讲的是若是类中没写构造函数类会自动给默认构造函数函数里面没内容所以起不到给类变量初始化的作用

c 复制代码
///typedef int STDataType;
////class Stack
////{
////public:
////	Stack(int n = 4)
////	{
////		cout << "Stack(int n = 4)" << endl;
////		_a = (STDataType*)malloc(sizeof(STDataType) * n);
////		if (nullptr == _a)
////		{
////			perror("malloc申请空间失败");
////			return;
////		}
////		//构造函数没有返回类型,但可以单独return;结束函数
////		_capacity = n;
////		_top = 0;
////	}
////
////	//析构函数
////	//构成
////	//~类名 + ()
////	//{
////	//free(类变量);
////	//........
////	//}
////	//析构函数自动调用
////	//构造函数类的实例化调用
////	~Stack()
////	{
////		cout << "~Stack()" << endl;
////		free(_a);
////		_a = nullptr;
////		_capacity = _top = 0;
////	}
////private:
////	STDataType* _a;
////	int _top;
////	int _capacity;
////};

这里是构造栈时构造函数的使用方式

这里还提到了析构函数

析构函数作用:自动调用将类变量中开辟空间的变量销毁

析构函数结构

~类名()

{

free(类变量);

XXXXX

}

最后调用

函数声明的顺序与构造函数调用成正比与析构函数调用成反比


本文到这里就告一段落,希望对你有所帮助,感谢观看!

相关推荐
泡海椒1 小时前
Java 后端最优 PDF 导出方案:jquick-pdf 项目引入与快速测试
java·开发语言·pdf
钓鱼的肝1 小时前
csp-j-s总结(1)
c++·笔记·算法·青少年编程
Evand J1 小时前
【MATLAB例程】二维Astar路径规划与AOA-TDOA定位仿真。完整代码,附下载链接。包运行成功,带中文注释
开发语言·matlab·路径规划·代码·定位·融合定位
李永奉1 小时前
中科蓝讯SDK开发-耳机项目功耗问题排查教程
c语言·开发语言·嵌入式硬件·物联网·智能手机
linx2952 小时前
第七章 · 标准库容器、算法与 ranges
c语言·开发语言·数据结构·c++·算法
钓鱼的肝2 小时前
csp-j-s总结(4)
c++·经验分享·笔记·算法
峥嵘life2 小时前
2026华为AI码道 CodeArts 使用分享:Windows端 + 服务器CLI 实战总结
android·大数据·开发语言·python
x秀x2 小时前
sam3新手使用教程
开发语言·python
Java后端的Ai之路2 小时前
大模型LLM评估完全指南
开发语言·人工智能·python·llm·评估