上//
- this对象
- 实参和形参都不能写,函数内部可以调用this
- This一般存在函数栈帧中,少部分用寄存器
- const Data*p 这里指针所指的对象固定,即对象不可修改
- Data* const p 这里指针的指向固定
- c和c++的stack对比
- c是散装,c++是封装
- c需要typedef,c++可以直接用类名
- c++有缺省参数,较方便(如开空间默认n=4)
- c++不用传地址,编译器帮你传this
- c可以随便读数据,如a.[top-1],c++不能访问private
- 面向对象的三大特征
- 封装
- 继承
- 多态
- 运算符重载
- 是一种拥有特殊名字的函数
- 名字由operator和运算符构成
实例:bool oprator==(Date d1,Date d2)
- operator==(d1,d2)等价于d1==d2
- 如果重载运算符函数是成员函数,则二元函数只用传一个参数,第一个运算对象由this隐式给出
- 有5个运算符不能重载:.* :: sizeof ?: .
- 重载运算符至少有一个类类型的参数
- 全局函数和对象函数同时存在时,运算符重载优先调用对象函数;
- 赋值运算符重载
完成两个已经存在的对象之间的拷贝
拷贝构造
一个存在的对象拷贝初始化给另一个要创建的对象
(1)参数类型:const T&,传递引用可以提高传参效率
(2)返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
(3)检测是否自己给自己赋值
(4)返回*this :要符合连续赋值的含义
Date& operato
(5)前置++和后置++重载
Date& operator++()
{
_day += 1;
return *this;
}
++d1
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
d1++
6.函数指针类型(若是成员函数,则在pf前加A::)
(1)void func()
(2)Typedef void(*PF)();Typedef void(A::*PF)()
void(*pf)()=nullptr;等价于PF pf=nullptr;
void(A::*pf)()=nullptr
(3)普通函数函数名即为函数指针,成员函数要加&才是函数指针;
(4)普通函数回调(*pf)(),成员函数回调(st1.*pf)()
中//六个默认成员函数
- 构造函数
不是开空间,而是初始化(局部变量的空间在创造栈帧时就已经开好)
- 特点:函数名和类名相同
- 无返回值也无void
- 实例化时系统会自动调用
- 可以重载
重载时该怎么调用?(以date类为例)
class Date
{
public:
Date()
{
_year = 2024;
_month = 07;
_day = 17;
}
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
无参:Date d1;
有参 Date d1(2025, 4, 25);
Ps;这里的Date是类而不是函数名,d1是对象;即在声明对象时自动初始化;
Ps:最好用全缺省,此时不能用无参,否则会有函数歧义
- 不写时,编译器会自动生成一个无参的构造函数
- 无参构造函数,全缺省构造函数,编译器自动生成的构造函数都叫默认构造函数
即不传参的都叫默认构造,三者有且只有一个存在
- 编译器生成的构造函数,大多数没有初始化的功能,少部分对内置类型的变量会处理。
特例:class MyQueue
{
private:
Stack _pushst;
Stack _popst;
};
若stack构造函数已经写好,编译器生成的queue的构造函数会自动调用,初始化两个栈
即对于自定义类型的成员变量,要求调用这个成员变量的默认构造函数初始化。
- 析构函数
- 析构函数名是在类名前加上字符~
- 无参也无返回值
- 一个类只有一个析构函数
- 对象生命周期结束时,系统会自动调用析构函数
- 不写时,编译器自动生成的析构函数不会对内置类型处理,自定义类型成员会调用它的析构函数
- 后定义的先析构;
~Stack()
{
if (_array) {
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
- 拷贝构造构造
如果一个构造函数的以第一个参数是自身类型的引用,且任何额外参数都有默认值,则此构造函数叫做拷贝构造函数
(1) 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用.
- 若未显式定义,编译器会生成默认的拷 贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
- 浅拷贝直接复制地址,共享一块内存,深拷贝则是拷贝资源,互不干扰。
- 需要实现析构的类,也需要实现显示的拷贝构造,即深拷贝
- 两种方式调用拷贝构造:Stack st1(st2)或者Stack st1=st2
Date(const Date& d) //
{
_year = d._year;
_month = d._month;
_day = d._day;
}
下//进阶
- 再识构造函数
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
};
【注意】
(1)每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
(2) 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量/ /const成员变量// 自定义类型成员(且该类没有默认构造函数时)
- 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。
- Const
2.1 概念
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用
static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化
面试题:实现一个类,计算程序中创建出了多少个类对象。
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;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制
- 友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以
友元不宜多用。
友元分为:友元函数和友元类
- 友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在
类的内部声明,声明时需要加friend关键字。
- 友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递
如果C是B的友元, B是A的友元,则不能说明C时A的友元。
友元关系不能继承,在继承位置再给大家详细介绍。
4.内部类
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,
它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越
的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访
问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。
5.匿名对象