目录
类的定义格式
class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意定义结束时后面分号不省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的方法或成员函数。
- //text.cpp
- #include
- using namespace std;
-
- class Stack
- {
-
- //成员变量
- int* a;
- int top;
- int capacity;
- //成员函数
- void Push()
- {
-
- }
- void Pop()
- {
-
- }
- };//分号不能省略
- int main()
- {
-
-
- return 0;
- }
- //为区分成员变量,一般前面加_
- //成员变量
- int* _a;
- int _top;
- int _capacity;
C++一种实现封装的方式,用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用
- //text.cpp
- #include
- using namespace std;
-
- class Stack
- {
-
-
- ///
-
- void Push()
- {
-
- }
- //Push 没给限定符 class默认私有 private
- ///
- public:
- void Pop()
- {
-
- }
- int Swap()
- {
-
- }
- //Pop和Swap 被public修饰,直到下一个限定符出现之前都为公有
- ///
- protected:
- int add();
- //add 被public修饰,直到下一个限定符出现之前都为保护
- /// /
-
- private:
-
- int* _a;
- int _top;
- int _capacity;
- //成员变量被private修饰,直到}结束都为私有
- };
- int main()
- {
- Stack st;
- //公有可以访问
- st.Pop();
- st.Swap();
- //私有不可访问
- st._top;
-
-
-
- return 0;
- }
额外注意:
类定义了一个新的作用域,类所有成员都在类的作用域中,在类体外定义成员时,需要使用 ::作用域操作符指明成员属于哪个类域
类域影响的是编译的查找规则,下面程序中Init如果不指定类域Stack,那么编译器就会把Init当成全局函数,那么编译时找不到_top等成员,就会到类域去找
- //text.cpp
- #include
- using namespace std;
-
- class Stack
- {
-
-
- public:
- void Init(int x, int y);
-
- };
- void Stack::Init(int x, int y)
-
- {
- _top = x;
- _capacity = y;
- }
- int main()
- {
-
- return 0;
- }
注意:
- //text.cpp
- #include
- using namespace std;
-
- class Stack
- {
-
- //声明
-
- int* _a;
- int _top;
- int _capacity;
-
- };
-
- int main()
- {
-
- Stack::_top = 2024;
- //编译器报错,_top只是声明,并未实例化
-
- return 0;
- }
-
- //text.cpp
- #include
- using namespace std;
-
- class Stack
- {
-
-
- //声明
- int* _a;
- int _top;
- int _capacity;
-
- };
-
- int main()
- {
-
-
- Stack st;
- st._top=2024;
- //Stack实例化出st,系统已经给st分配内存了,可以存储数据,编译通过
-
- return 0;
- }
-
-
内存对齐规则
- class A
- {
- public:
- void Print()
- {
- cout << _ch << endl;
- }
- private:
- char _ch;
-
- int _i;
- };
- //_ch 是一个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存在_ch
- // _i是4个字节,默认对齐数是4,最大对齐数是4,所以开辟4个字节用来存储_i
- class B
- {
- public:
- void Print()
- {
- //。。。
- }
-
-
- };
- class B
- {
-
-
- };
- //B和C里面没有存储任何成员变量,只有一个函数,可成员函数不存对象里面
- // 按理来说是0,但是结构体怎么会没大小,为表示对象存在C++对这种规定大小为1,为了占位标识对象存在
编译器编译后,类的成员函数默认都会在形参第一个位置,增加一个当前类的指针,叫做this指针
例如Date类中的Init原型为 void Init(Date * const this,int year ,int month,int day),类的成员函数中访问成员变量,本质是通过this指针访问的,如Init函数中给_year赋值,this->_year=year
原型:
- class Date
- {
- void Print()
- {
-
- cout << _year << "\n" << _month << "\n" << _day << endl;
- }
- void Init( int year, int month,int day)
- {
- _year = year;
- _month = month;
- _day = day;
-
- }
-
- private:
- int _year;
- int _month;
- int _day;
-
- };
-
- Date d1;
- d1.Init(2024,7,10);
-
- d1.Print();
- Date d2;
-
- d2.Init(2024, 7, 9);
- d2.Print();
-
真实原型
- class Date
- {
- void Init(Date* const this,int year, int month,int day)
-
- {
- this->_year = year;
- this->_month = month;
- this->_day = day;
-
- }
- void Printf(Date* const this)
-
- {
-
- cout << this->_year << "\n" <<this-> _month << "\n" << this->_day << endl;
- }
- private:
- int _year;
- int _month;
- int _day;
-
- };
- Date d1;
- d1.Init(&d1,2024,7,10);
- d1.Print(&d1);
-
- Date d2;
- d2.Init(&d2,2024, 7, 9);
- d2.Print();
C++规定不准在实参和形参的位置写this指针(编译时编译器会处理),但是可以在函数体内显示使用this指针,this指针不能修改,但this指针指向的内容可以
this指针存在栈里
默认成员函数就是用户没有显示定义,编译器会自动生成的成员函数称为默认成员函数
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名字叫构造函数,但是构造函数的主要内容并不是开辟空间创造对象(我们平常使用的局部对象是栈帧创建时,空间就开好了),而是对象实例化时初始化对象。构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能,构造函数自动调用的特点就完美替代了Init
构造函数的特点:
- class Date
- {public:
- //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;
- }
- private:
- int _year;
- int _month;
- int _day;
-
- };
无参构造函数,全缺省构造函数,我们不写构造时编译器默认生成的构造函数,都叫做默认构造函数。但是这三个有且只能有一个存在,不能同时存在。无参构造函数和全缺省构造函数虽然构成函数重载,但是调用时会存在歧义。注意并不是只有默认构造函数就是编译器默认生成的那就是构造函数,无参构造函数,全缺省构造函数也是默认构造函数,总结一下就是不传参就能调用
我们不写,编译器默认生成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始化是不确定的,看编译器。
- //text.cpp
- #include
- using namespace std;
- typedef int STDataType;
- class Stack
- {
- public:
- Stack(int n = 4)
- {
- _a = (STDataType*)malloc(sizeof(STDataType) * n);
- if (nullptr == _a)
- {
- perror("malloc申请失败");
- }
- _capacity = n;
- _top = 0;
- }
-
- private:
- STDataType* _a;
- size_t _capacity;
- size_t _top;
- };
- //两个Stack实现队列
- class MyQueue
- {
- private:
- int size;
- Stack pushst;
- Stack popst;
- };
-
- int main()
- {
-
- MyQueue my;
-
-
- return 0;
- }
-
C++把类型分为自定义类型和内置类型(基本类型)。内置类型就是语言提供的原生数据类型,如int/char/double/指针等,自定义类型就是我们使用class/struct等关键字自己定义的类型。这里构造函数自动初始化,VS也将内置类型size初始化了,不同的编译器初始化值不同,C++并没有规定

对于自定义类型成员变量,要求调用这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认的构造函数,那么就会报错,我们要初始化这个成员变量,需要用初始化列表才能解决
总结:大多数情况下,构造函数都需要我们自己去实现,少数情况类似MyQueue且Stack有默认构造函数时,MyQueue自动生成就可以用
- ~Stack()
- {
- free(_a);
- _a = nullptr;
- _top = _capacity = 0;
- }
析构函数的特点:
1.析构函数名是在类名前面加上字符~
2.无参无返回值(与构造函数一致)
3.一个类只能有一个析构函数。若未显示定义,系统也会自动生成默认的析构函数
4.对象声明周期结束时,系统会自动调用析构函数,
5.跟构造函数类似,我们不写编译器自动生成的析构函数对内置类型成员不做处理,自定义类型成员会调用其他的析构函数
6.还需注意的是我们显示析构函数,对于自定义类型成员也会调用他的析构函数,也就是说自定义类型成员无论什么情况都会自动调用析构函数。
- //text.cpp
- #include
- using namespace std;
- typedef int STDataType;
- class Stack
- {
- public:
- Stack(int n = 4)
- {
- _a = (STDataType*)malloc(sizeof(STDataType) * n);
- if (nullptr == _a)
- {
- perror("malloc申请失败");
- }
- _capacity = n;
- _top = 0;
-
-
- }
-
- ~Stack()
- {
- free(_a);
- _a = nullptr;
- _top=_capacity=0;
- }
- private:
- STDataType* _a;
- size_t _capacity;
- size_t _top;
-
- };
- //两个Stack实现队列
- class MyQueue
- {public:
- //编译器默认生成MyQueue的构造函数调用了Stack的构造,完成了两个成员的初始化
- //编译器默认生成MyQueue的析构函数调用了Stack的析构,释放了Stack内部的资源
- //显示写析构也会调用Stack的析构
- ~MyQueue()
- {
- cout << "~MyQueue" << endl;
- }
- private:
-
- Stack pushst;
- Stack popst;
-
- };
-
-
- int main()
- {
-
- MyQueue my;
-
-
- return 0;
- }
MyQueue里的析构啥也没干,但是C++规定会调用其他的析构来释放内存

如果没有申请资源时,析构可以不写,直接使用编译器生成的默认析构函数,如Date,如果默认生成的析构可以用,也就不需要显示写析构如MyQueue,但是有资源申请时,一定要直接写析构,否则会造成资源泄漏如Stack
-
-
- bool operator<(Date d1, Date d2)
- {
-
- }
- bool operator==(Date d1,Date d2)
- {
- return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
- }
- //text.cpp
- #include
- using namespace std;
-
- class Date
- {
- public:
- Date(int year, int month, int day)
- {
- _year= year;
- _month = month;
- _day = day;
-
- }
-
-
-
- int _year;
- int _month;
- int _day;
-
- };
-
-
- int GetMonthDay(int year ,int month)
- {
- static int monthDayArray[13]={-1,31,28,31,30,31,30,31,31,30,31,30,31};
- if(month==2&&((year%4==0&&year%100!=0)||(year%400==0)
- {
- return 29;
- }
- return monthDayArray[month];
- }
-
- bool operator<(Date d1, Date d2)
- {
- Date tem=*this;
- tem-=day;
- return tmp;
- }
- //天数是否相等
- bool operator==(Date d1,Date d2)
- {
- return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
- }
- //日期-天数
- Date& Date:operator-=(int day)
- {
-
- _day-=day;
- while(_say<=0)
- {
- --_month;
- if(_month==0)
- {
- _month=12;
- --_year;
- }
- }
- _day+=GetMonthDay(_year,_month);
- }
- //Date 加一个天数
- Date& Date::operator+=(int day)
- {
-
- _day+=day;
- while(_day>GetMonthFay(_year,_month))
- {
- _day-=GetMonthDay(_year,_month);
- ++month;
- if(_month==13)
- {
- _year++;
- _month=1;
- }
- }
- return *this;
- )
- //日期比大小
- bool Date::operator<(const Date& d)
- {
- if(_year
- {
- return ture;
- }
- elae if(_year==d,_year)
- {
- if(_month
- {
- return true;
- }
- else if(_month==d._month)
- {
- return _day
- }
- }
- return false;
- }
-
- //日期比大小
- bool Date :: operator<=(const Date&d)
- {
- return *this
this==d; - }
- //日期比大小
- bool Date :: operator>=(const Date&d)
- {
- return !(*this<=d);
- }
- int main()
- {
-
- Date d1(2024, 7, 10);
- Date d2(2024,7,9);
- //两种用法都可以
- d1 == d2;
- operator==(d1 , d2);
- return 0;
- }
- 如果一个重载运算符函数是成员函数,则他的第一个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数比运算对象少一个
- 运算符重载以后,其优先级和结合性与内置类型运算保持一致
- 不能通过连接语法中没有的符合来创建性的操作符:比如operator@
- .* :: sizeof ?: . 以上五个不能重载
- 一个类需要重载哪些运算符,是看哪些运算符重载后有意义,比如Date类重载operator-就有意义,operator+就没有意义
取地址运算符重载
const 成员函数
- 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后面
- void Print() const
- {
- cout << _year << _month << _day << endl;
- }
- const 实际修饰该成员函数隐含的this指针,表明该成员函数中不能对类的任何成员进行修改。const修饰Date类的Print成员函数,Print隐含的this指针由Data* const this变成为const Data* const this

取地址运算符重载
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,一般两个函数编译器自动生成的就够我们用,不需要自己去实现。除非一些很特殊的场景,比如说我们不想被别人取到当前类对象的地址,就可以自己去实现一份,胡乱返回一个地址
- Date* Date :: operator&(const Date & d)
- {
- return (Date*)0x15612FE40;
- }
- const Date* Date :: operator&(const Date& d) const
- {
- return (Date*)0x15612FE30;
- }
-
相关阅读:
python切片选取行和列
深入探究Linux文件:.sh、.swp文件的作用与意义 (linux .sh.swp)
java源码系列:HashMap源码验证,在JDK8中新增红黑树详解
jmeter压测工具的使用
Vue中如何获取dom元素?
uniapp微信小程序局部刷新,无感刷新,修改哪条数据刷新哪条
CubeMX+BabyOS 使用方法
使用bloodyAD对域属性进行查询与修改
C#高级用法
K8s云原生存储Rook详解
-
原文地址:https://blog.csdn.net/2302_81171591/article/details/140346308