权限相关
|| 三种权限
在C++中 struct和class唯一的区别就在于 默认的访问权限不同
class默认是私有权限, struct默认是公共权限
三种权限
公共权限 public 类内可以访问 类外可以访问
私有权限 private 类内可以访问 类外不可以访问 (同时不可继承)
保护权限 protected 类内可以访问 类外不可以访问 (但可以被继承)
class Person {
int m_age = 1; 等同于:// private:
// int int m_age = 1;
};
构造函数与析构函数
|| 一个类中默认封装的函数:
1,无参构造函数
2,拷贝构造函数(浅拷贝)
3,析构函数
|| 深究构造函数:
构造函数的格式 : 1,无返回类型 2,函数名必须为类名
构造函数的分类: 1,有无参构造函数 2,拷贝构造函数
构造函数的调用方式 1,括号法 2,隐式法 3,显示法
|| 拷贝构造函数的使用情景:
1,复制一个相同对象时
Person p1(p2);
2,(值传递)以对象作为函数参数时
void doWork(Person p1) == void doWork (Person p1 = p) // p1是p的副本
3, 接收一个函数的返回对象时
Person p2 = doWork();
Person doWork()
{
Person p1;
return p1;
}
|| 拷贝构造函数的浅拷贝与深拷贝:
在涉及内存释放的问题时,浅拷贝带来的重复释放堆区问题
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
总结一下就是:使用new创建变量的 拷贝构造函数 是 深拷贝构造函数,非则否
根本区别在于数据存放在堆区还是栈区(是不是new出来的)浅拷贝共用一段内存中的
数据,深拷贝使用另外一段内存中的数据(程序员使用new开辟的)。
Person(const Person& p) {
cout << "拷贝构造函数!" << endl;
m_age = p.m_age;
//m_height = p,height; --- 编译器默认写法
m_height = new int(*p.m_height)
}
|| 构造函数的初始化列表:(类的另一种初始化方式)
//Person(int a, int b) : p_A(a), p_B(b) {}
Person(int a, int b){
p_A = a;
p_B = b;
}
|| 当一个类的对象作为一个类的属性时:
构造顺序:先支类再主类
析构顺序:先主类再支类(相反)
限定符
//先介绍一下this指针
|| this指针是指向调用该函数的对象的指针
|| this指针的本质:是一个指针常量(指针的指向不可以改变,即只可以指向调用函数的对象)
用处:
1,防止名称冲突。this -> age = age;
2,*this 可以让函数返回对象本身。
//tips:this 返回值必须是Person的引用,防止创造新的对象的副本
Person & addPersonAge (Person p)
{
......
return *this;
}
static
|| 静态成员变量 static — 内存固定在一处的变量 (改变数据以后就会一致使用新数据)静态成员变量不被一个对象专有,而是所有的该类对象共享
静态成员变量的特点
1,所有对象共享同一份数据
2,类内声明,类外初始化
// 3,在编译阶段分配内存(全局区)
|| 静态成员函数 static — 一种针对静态变量进行操作的函数,针对固定内存的变量进行操作的函数 静态成员函数不被一个对象专有,而是所有的该类对象共享
静态成员函数的特点
1,所有对象共享同一个函数
2,静态成员函数只能访问静态成员变量
|| 只有非静态成员变量才储存在类上;非静态成员函数和静态成员都不出存在类上
const
|| const 修饰成员变量 ------ 常数 ------ 将数据设置为只读状态
const m_Age;
|| const 修饰成员函数 ------ 常函数 — 函数将不可修改成员属性的值
本质上是修饰该函数的 this指针,使之变成常量指针 (最终变成了常量指针常量)
void addPersonAge() const {
}
|| const 修饰变量 ------------ 常对象 ----对象将不可修改成员属性
常对象实际上就是常函数加常数的结合
const Person p1;
|| 常对象只能调用常函数,不用调用普通成员函数
|| mutable 修饰的成员变量无论何时何地都可以修改 (mutable — 易变的)
friend
友元(就是友元函数/友元类) 让一个函数或者类变成某个类的朋友,访问另一个类中私有成员
友元的三种实现
1,全局函数做友元
2,类做友元
3,成员函数做友元
class Building
{
// 告诉编译器 goodGay全局函数 是 Building类的好朋友
// friend + 函数声明
1,friend void goodGay(Building * building);
2,friend class Person
3,friend void Person::add()
public:
private:
}
运算符重载
实质上是构造 特殊函数operator 这个函数有特殊的调用方式,可以被简化为运算符形式
//一般可以使用成员函数或者全局函数进行重载
简化:Preson p3 = p1 + p2 --- Person P3 = p1.operator+(p2) --- 成员函数
--- Person P3 = Operator(p1, p2) --- 全局函数
|| 算数运算符重载
示例
//成员operator函数 实现+运算符重载
Person operator+(const Person& p) {
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
//全局operator函数 实现+运算符重载
Person operator+(const Person& p2, int val)
{
Person temp;
temp.m_A = p2.m_A + val;
temp.m_B = p2.m_B + val;
return temp;
}
|| 左移运算符<< 的重载
注意事项:1,ostream为输出流类
2,传入输出流参数时,因为cout全局中只能有一个,所以必须使用(别名)
3,为了达到cout << p 的实际效果,只能使用全局函数重构(成员函数 p<<cout)
4,为了达到cout <<p<< endl的实际效果,必须返回一个输出流(链式编程思想)
ostream& operator<< (ostream &cout, Person &p) {
cout << "m_A = " << p.m_A << "m_B = " << p.m_B;
return cout;
}
|| 递增运算符
注意事项:
前置递增和后置递增的区分:
一:函数名称上
前置递增运算符对应的函数 operator++()
后置递增运算符对应函数 operator+(int) ---系统会懂
二: 功能实现上
前置递增先实现++ 后 return
后置递增先记录当前值 后实现++ 最后返回原记录值
三:返回类型上
前置:必须返回别名!保持只对同一个数操作
后置:必须返回值!因为已经通过局部对象记录过了值,且局部对象会被释放
//前置++
MyInteger& operator++() {
//先++
m_Num++;
//再返回
return *this;
}
//后置++
MyInteger operator++(int) {
//先返回
MyInteger temp = *this; //记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;
m_Num++;
return temp;
}
|| 类的赋值运算符的重载
注意事项:
同样,在涉及内存释放问题时,对象的赋值操作也会有深拷贝和浅拷贝的问题(同拷贝构造)
|| 赋值中的深拷贝操作:1,判断成员变量是否在堆区中,并清空 2,重新new
|| 复习:拷贝构造中的深拷贝操作:只需new来开辟内存存放变量(未构造无需清空)
class Person{
int *m_A
public:
Person(int a){
m_A = new int(a);
}
Person operator=(Person p){
// 1,先判断本对象是否在堆区有内存
if(*m_A != NULL)
{
delete m_A;
m_A == NULL;
}
// 2,深拷贝 在堆区重新开辟内存
m_A = new int(*p.m_A);
return *this;
}
}
|| 关系运算符的重载
bool operator==(Person & p)
bool operator!=(Person & p)
bool operator>=(Person & p)
|| 函数调用符()的重载
|| 由于重构后的 对象() 使用起来特别想像一个函数调用函数(),故也称作 — 仿函数
class MyPrint
{
public:
void operator()(string text)
{
cout << text << endl;
}
};
|| 匿名对象写法:类名() — 自动创建一个对象,并在该行结束后立刻被释放