C++之类与对象篇重点代码
·
C++之类与对象篇重点代码
《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 构造函数
- 案例一
//构造函数
#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;
}
运行结果:
- 案例二
#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++的问题,也欢迎一起在评论区交流探讨(☆▽☆),咱们下次分享再见~
更多推荐


所有评论(0)