《C++之类与对象》和《C++之再谈类与对象》这两篇文章详细叙述了C++编程中类与对象的基础知识以及进阶拓展知识。如果小伙伴们仔细阅读过那两篇文章,我相信你们现在已经完全理解并掌握了类与对象的全部知识啦~ 本篇文章是对《C++之类与对象》和《C++之再谈类与对象》那两篇文章的重点知识代码补充,让我们一起开启C++中类与对象的代码之旅吧(ง •_•)ง

1. 类

1.1 类的引入

//C++兼容c语言。struct以前的用法都可以继续用,同时struct升级成了类
#include<stdio.h>
#include <malloc.h>
#include <iostream>
#include "Func.h"
#include "stdlib.h"
using namespace std;
struct stack
{
public:
	//成员函数(c++用法)
	void Init(int defaultCapacity = 4)
	{
		a = (int*)malloc(sizeof(int) * defaultCapacity);
		if (nullptr == a)
		{
			perror("malloc申请空间失败");
			return;
		}
		capacity = defaultCapacity;
		top = 0;
	}

	void push(int x)
	{
		a[top++] = x;
	}

	int Top()
	{
		return a[top - 1];
	}
	void Destory()
	{
		free(a);
		a = nullptr;
		top = capacity;
	}
private:
	//成员变量
	int* a;
	int top;
	int capacity;
};

int main()
{
	struct stack s1;
	s1.Init(20);
	
	stack s2;//c++用法,s2是类实例化对象/对象定义
	s2.Init(4);
	s2.push(1);
	cout << s2.Top() << endl;
	cout << sizeof(s2) << endl;//对象中指存储成员变量大小,不存储成员函数
	s2.Destory();
	return 0;
}

运行结果
在这里插入图片描述

1.2 类的定义

#pragma once  //Func.h中
#include<iostream>
using namespace std;


class stack
{
public:
	//成员函数(c++用法)
	void Init(int defaultCapacity = 4);//长的函数会声明和定义分离
	

	void push(int x)//在类里面定义的函数默认就是inline
	{
		a[top++] = x;
	}
 
	int Top()
	{
		return a[top - 1];
	}

	void Destory()
	{
		free(a);
		a = nullptr;
		top = capacity;
	}

	//成员变量
	int* a;
	int top;//声明,声明不能存数据
	int capacity;
};


#include<stdio.h>  //Func.cpp中
#include <malloc.h>
#include <iostream>
#include "Func.h"

void stack::Init(int defaultCapacity)//声明和定义分离,要加类域(默认参数不能重定义2次)
{
	a = (int*)malloc(sizeof(int) * defaultCapacity);
	if (nullptr == a)
	{
		perror("malloc申请空间失败");
		return;
	}
	capacity = defaultCapacity;//声明和定义分离,要加类域
	top = 0;
}

int main()
{
	struct stack s1;
	s1.Init(20);

	stack s2;//c++用法
	s2.Init(4);
	s2.push(1);
	std::cout << s2.top << std::endl;
	s2.Destory();
	return 0;
}



运行结果
在这里插入图片描述

1.2.1 类的优先级


//补充:先局部在类域
class Date
{
public:
	void Init(int year)
	{
		_year = year;//先局部在类域
	}
private:
	int _year;
	int _month;
	int _day;
};

1.3 类对象模型

//计算类对象的大小
#include <iostream>
using namespace std;

class A1 {
public:
	void f1() {}
private:
	char _ch;//注意:对齐数 = 编译器默认的一个对齐数(8) 与 该成员大小的较小值
	int _a;
};

// 类中仅有成员函数
class A2 {
public:
	void f2() {}//没有成员变量的类对象,需要1字节,为了占位表示对象存在。不存储有效数据
};
// 类中什么都没有---空类
class A3
{};

int main()
{
	cout << sizeof(A1) << endl;
	cout << sizeof(A2) << endl;
	cout << sizeof(A3) << endl;
	return 0;
}

运行结果
在这里插入图片描述

1.4 类成员函数的this指针

//隐含的this指针
#include <iostream>
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;
	}
	//void Print(Date* const this)//编译器对成员函数的处理,this不能在形参和实参显示传递(写出来),但是可以在函数内部显示使用
	//{
	// cout << this << endl;
	//	cout <<this-> _year << "-" << this->_month << "-" << this->_day << endl;
	//}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1, d2;
	d1.Init(2022, 1, 11);
	d2.Init(2022, 1, 12);
	d1.Print();
	//d1.Print(&d1);
	d2.Print();
	return 0;
}

运行结果
在这里插入图片描述

1.5 类的6个默认成员函数

1.5.1 构造函数

  1. 案例一
//构造函数
 #include <iostream>
using namespace std;

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;//内置类型成员
	int _month;
	int _day=2;//在成员声明的时候给缺省值
};

int main()
{
	Date d1;
	Date d2;
	d1.Print();
	return 0;
}

运行结果
在这里插入图片描述

  1. 案例二
#include <iostream>
using namespace std;

class Date
{
public:
	Date()// 默认构造函数
	{
		_year = 1;
		_month = 1; 
		_day = 1;
	}

	Date(int year, int month = 2, int day = 1)//单参数构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year =1;
	int _month =1;//内置类型,声明如果给了缺省值会用缺省值
	int _day =1;
};

int main()
{
	Date d1;//d1是自定义类型成员,会调用默认构造函数
	//Date d1();错误,会和函数声明有冲突,编译器不好识别d1是对象还是函数名
	Date d2(2025, 9, 28);//对象+参数列表
	Date d3(2012);
	Date d4(2004, 7);//year是传参,month是给参数就用没给就用缺省参数(2)
	d1.Print();
	d2.Print();
	d3.Print();
	d4.Print();
	return 0;
}

运行结果
在这里插入图片描述

1.5.2 析构函数

//析构函数
 #include <iostream>
using namespace std;

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		cout << "Stack()" << endl;
		_a = (int*)malloc(sizeof(int) * capacity);//有动态申请资源,需要显示写析构函数释放资源
		if (nullptr == _a)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	
	}
	
	~Stack()
	{
		cout << "~Stack()" << endl;
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_capacity = 0;
			_top = 0;
		}
	}
private:
	int* _a = nullptr;
	int _capacity;
	int _top = 0;
};

class MyQueue
{
private:
	Stack _pushst;//需要释放资源的成员都是自定义类型,不需要写析构
	Stack _popst;
};
int main()
{
	Stack st1;
	MyQueue q;
	return 0;
}

运行结果
在这里插入图片描述

1.5.3 拷贝构造函数

//拷贝构造  Stack需要拷贝构造 Date可以值拷贝不用拷贝构造也可以
#include<stdio.h>
#include <malloc.h>
#include <iostream>
#include "stdlib.h"
using namespace std;
class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	Date(Date& d)//拷贝构造d2(d1)  d是d1的别名 d2传给this
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print() const  // const保证不修改对象
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
	
		_a = (int*)malloc(sizeof(int) * capacity);//有动态申请资源,需要显示写析构函数释放资源
		if (nullptr == _a)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	
	}

	void push(int x)
	{
		_a[_top++] = x;
	}

	Stack(const Stack& st) //深拷贝
	{
		_a = (int*)malloc(sizeof(int) * st._capacity);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		memcpy(_a, st._a, sizeof(int) * st._top);
		_top = st._top;
		_capacity = st._capacity;
	}
	
	~Stack()
	{
	
		if (_a)
		{
			free(_a);
			_a = nullptr;
			_capacity = 0;
			_top = 0;
		}
	}

	void print() const
	{
		cout << "Stack元素: [";
		for (int i = 0; i < _top; ++i)
		{
			cout << _a[i];
			if (i != _top - 1) cout << ", ";
		}
		cout << "], 容量: " << _capacity << ", 元素个数: " << _top << endl;
	}

private:
	int* _a = nullptr;
	int _capacity;
	int _top = 0;
};


int main()
{
	Date d1(2025, 9, 28);
	Date d2(d1);//拷贝构造,可以不写拷贝构造函数 值拷贝/浅拷贝默认生成的拷贝就可以用
	d2.Print();


	Stack st1;
	st1.push(1);
	st1.push(2);
	Stack st2(st1);//必须自己实现深拷贝
	st2.print();
	return 0;
}

运行结果
在这里插入图片描述

1.5.4 赋值运算符重载

1.重载运算符的引入

//比较日期大小
#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}


	bool operator<(const Date& x)//operator运算符重载,至少有一个自定义类型重载
	{
		if (_year < x._year)//操作符是几个操作数,重载函数就有几个参数(this,x)
		{
			return true;
		}
		else if (_year == x._year && _month < x._month)
		{
			return true;
		}
		else if (_year == x._year && _month == x._month && _day < x._day)
		{
			return true;
		}
		return false;
	}


	int _year;
	int _month;
	int _day;
};
 
bool Less(const Date& x, const Date& y)//函数Less比较大小
{
	if (x._year < y._year)
	{
		return true;
	}
	else if (x._year == y._year && x._month < y._month)
	{
		return true;
	}
	else if (x._year == y._year && x._month == y._month && x._day < y._day)
	{
		return true;
	}
	return false;
}

bool operator<(const Date& x, const Date& y)//operator运算符重载,至少有一个自定义类型重载 
{
	if (x._year < y._year)
	{
		return true;
	}
	else if (x._year == y._year && x._month < y._month)
	{
		return true;
	}
	else if (x._year == y._year && x._month == y._month && x._day < y._day)
	{
		return true;
	}
	return false;
}


int main()
{
	Date d1(2025, 9, 29);
	Date d2(2025, 9, 30);
	cout << Less(d1, d2) << endl;
	cout << (d1 < d2) << endl;//被转换成调用operator<(d1,d2)
	cout << (operator<(d1, d2)) << endl;
	cout << d1.operator<(d2) << endl;
	return 0;
}

运行结果
在这里插入图片描述

2.赋值运算符重载

//赋值运算符重载
//Date和MyQueue不需要我们自己实现赋值重载,Stack需要我们自己实现,因为默认生成浅拷贝
#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//Date(const Date& d)//拷贝构造d2(d1)  d是d1的别名 d2传给this
	//{
	//	cout << "Date(Date & d)" << endl;
	//	_year = d._year;
	//	_month = d._month;
	//	_day = d._day;
	//}

	Date& operator=(const Date& d)//赋值重载,不能写在全局。返回值Date会有拷贝构造效率低,所以应该用Date&做返回值  
	{
		if (this != &d)  //最好不让d1 = d1自己给自己赋值;
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
			return *this;
		}
	}

	void Print() const  // const保证不修改对象
	{
		
cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2023, 7, 7);
	Date d2(2025, 9, 29);
	d1 = d2;//已经存在的两个对象之间的复制拷贝,本质运算符重载“operator=”是默认成员函数不写编译器会自动生成
	d1.Print();
	return 0;
}

运行结果
在这里插入图片描述

1.5.5 取地址及const取地址运算符重载

1.取地址及const取地址运算符重载

//取地址运算符重载,const取地址运算符重载
#include <iostream>
using namespace std;

class Date
{
public:
	Date* operator&()//我们不写编译器自动生成
	{
		return this;//把this设置为nullptr则普通对象取不到地址
	}

	const Date* operator&()const//const取地址运算符重载
	{
		return this;
	}
	
	
private:
	int _year = 1;
	int _month = 1;//内置类型,声明如果给了缺省值会用缺省值
	int _day = 1;
};

int main()
{
	Date d1;
	cout << &d1 << endl;//自定义类型不能直接用相关的&运算符 
	return 0;

运行结果
在这里插入图片描述

2. const成员函数

//const用法
#include <iostream>
using namespace std;

int main()
{
	const int a = 10;
	int b = a;//a拷贝给b b的改变不影响a 不涉及权限放大
	cout << b;

	//const int x = 10;
	//int& y = x;// 错误的。y是x的别名,y的改变会影响x 涉及权限放大

	//const int x = 10;
	//int* pb = &x;//错误的 涉及权限放大
	return 0;

}

运行结果
在这里插入图片描述

3. 再谈构造函数

初始化列表。

3.1 初始化列表案例一

//初始化列表
class A
{
public:
	A(int a)//全缺省
		:_a(a)
	{}
private:
	int _a;
};

class B
{
public:
	B(int a, int ref)//初始化列表:对象的成员定义的位置
		:_aobj(a)//没有默认构造就必须显式地写调用
		, _ref(ref)
		, _n(10)
	{}
private:
	A _aobj; // 没有默认构造函数(全缺省和无参) 必须在定义的时候初始化,使用初始化列表
	int& _ref; // 引用  必须在定义的时候初始化
	const int _n; // const  必须在定义的时候初始化
	
	int _x = 1;// 1是缺省值,缺省值是给初始化列表的。内置类型本身不处理,但写了缺省值就会用缺省值初始化
};

int main()
{
	B bb(10, 1);//对象整体定义
	return 0;
}

运行结果
在这里插入图片描述

3.2 初始化列表案例二

//初始化列表下面的{}也可以写需要的函数
#include<stdio.h>
#include <malloc.h>
#include <iostream>
#include "stdlib.h"
using namespace std;
class Stack
{
public:
	Stack(int capacity = 10)
		:_a((int*)malloc(sizeof(int) * capacity))
		,_top(0)
		,_capacity(capacity)
	{
		if (nullptr == _a)
		{
			perror("malloc失败");
			exit(-1);
		}
		memset(_a, 0, sizeof(int)*capacity);//数组初始化为0 
	}

private:
	int* _a;//最好声明顺序和初始化列表定义顺序保持一致
	int _top;
	int _capacity;
	
};

int main()
{
	Stack st(20);
	
	return 0;
}

运行结果
在这里插入图片描述

3.3 初始化列表案例三

//不一定所有成员都在初始化列表里
#include<stdio.h>
#include <malloc.h>
#include <iostream>
#include "stdlib.h"
using namespace std;

class AA
{
public:
	AA(int row=10, int col=5)
		:_row(row)
		,_col(col)
	{
		_aa = (int**)malloc(sizeof(int*) * row); //动态开辟二维数组, _aa不在初始化列表里在函数体内
		for (int i = 0; i < row; i++)
		{
			_aa[i] = (int*)malloc(sizeof(int) * col);
		}
	}
private:
	int** _aa;
	int _row;
	int _col;
};

int main()
{
	AA aa;
	return 0;
}

运行结果:
在这里插入图片描述

4. explicit关键字


//隐式类型转换  
#include <iostream>
using namespace std;
class A
{
public:  
	//explicit A(int a);// explicit不允许隐式类型转换
	A(int a)      //构造函数,可以初始化
		:_a()
	{
		cout << "A(int a)" << endl;
	}

	A(const A& aa) //拷贝构造 
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
private:
	int _a;
};

int main()
{
	A aa1(1);
	A aa2 = 2;//隐式类型转换,整形转换成自定义类型  编译器不能容忍先构造临时对象在拷贝构造 所以直接优化成直接构造
	const A& aa3 = 2;//临时对象具有常性 
	
	return 0;
}

运行结果
在这里插入图片描述

5. static成员

5.1 案例一

//static静态成员
#include <iostream>
using namespace std;
int _scount = 0;//统计A创建的对象的个数   全局变量的缺点:任何地方都可以随意改变
class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; } 
	~A() { --_scount; }
	//static int GetACount() { return _scount; }
private:
	//static int _scount;
};
//int A::_scount = 0;


A aa0;//全局定义一个对象

int main()
{
	cout <<__LINE__ << ":" << _scount << endl;
	A aa1;
	static A aa2;
	cout << __LINE__ << ":" << _scount << endl;
	
	return 0;
}

运行结果
在这里插入图片描述

5.2 案例二

//静态全局变量封装在类里
#include <iostream>
using namespace std;
class A
{
public:
	A() { ++_scount; }
	A(const A& t) { ++_scount; }
	~A() { --_scount; }
	
	static int GetACount() //静态成员函数没有this指针
	{ 
		return _scount; //_scount直接在外边访问不了,但是指定类域和访问限定符就可以访问
	}
private:
	int _a1 = 1; //成员变量(属于每个一个类对象,存储在对象里面)
	static int _scount; //静态成员变量(属于类的每个对象共享,存储在静态区)
};

int A::_scount = 0; //在全局位置,类外边定义,在外边访问不了但是指定类域和访问限定符就可以访问

A aa0;//全局定义一个对象 

int main()
{
	cout << __LINE__ << ":" <<A::GetACount() << endl;
	A aa1;
	static A aa2;
	cout << __LINE__ << ":" << A::GetACount() << endl;

	return 0;
}

运行结果
在这里插入图片描述

5.3 案例三

//设计一个类, 在类外边只能在栈上 或者 堆上创建对象
 //#include <iostream>
using namespace std;
class A
{
public:
	static::A GetStackObj() //构造栈对象,用static就不需要this调用
	{
		A aa;
		return aa;
	}

	static::A* GetHeapObj()//构造堆对象
	{
		return new A;
	}
private:
	A()//构造函数私有,谁都不能调用
	{}
private:
	int _aa1 = 1;
	int _aa2 = 2;
};

int main()
{
	//static A aa1;     //静态区
	//A aa2;          //栈
	//A* ptr = new A;//堆
	
	A::GetStackObj();
	A::GetHeapObj();

	return 0;
}

运行结果
在这里插入图片描述

6. 友元


//友元(友元函数,友元类)
 #include <iostream>
using namespace std;
class Time
{
	friend class Date; // 友元声明,声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量

public:
	Time(int hour = 0, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}

	void PrintTime() const
	{
		cout << _t._hour << ":" << _t._minute << ":" << _t._second << endl;
	}

private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d(2025, 10, 2);
	d.SetTimeOfDate(17, 0, 0);
	d.PrintTime();
	return 0;
}

运行结果
在这里插入图片描述

7. 内部类

//内部类
 #include <iostream>
using namespace std;
class A
{
private:
	static int k;//k没有存在对象里面 存在全局(静态区)
	int h;
public:
	class B // 内部类(B)天生就是外部类(A)的友元    B是公有内部类
	{
	public:
		void foo(const A& a)
		{
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	private:
		int b;
	};
};

int A::k = 1;

int main()
{
	cout << sizeof(A) << endl;
	A::B b;
	b.foo(A());

	return 0;
}

运行结果
在这里插入图片描述

8. 再次理解对象

匿名对象。

//匿名对象
 #include <iostream>
using namespace std;
class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

int main()
{
	A aa(1);//有名对象,生命周期在当前函数局部域
	A(2);//匿名对象 即用即销毁,生命周期在当前行
	//A& ra = A(1); //错误的,匿名对象具有常性
	const A& ra = A(1); //const引用延长匿名对象生命周期,生命周期在当前函数局部域
	return 0;
}

运行结果
在这里插入图片描述

写在最后:到这里,关于C++类与对象的重点代码就分享得差不多啦~类与对象是C++面向对象编程的核心基石,希望这些代码能帮助小伙伴们更扎实地掌握相关知识。后续在编程实践中,多运用类与对象的思想去解决问题,相信大家将对它们的理解会愈发深刻。若小伙伴们还有其他关于C++的问题,也欢迎一起在评论区交流探讨(☆▽☆),咱们下次分享再见~
在这里插入图片描述

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐