C和C++基础
// new/delete 是一套 会调用构造函数 与 析构函数 【C++标准规范】
// malloc/free是一套 不调用构造函数 与 析构函数 【C的范畴,虽然不推荐,但是也是可以的】
释放内存之后,
free()
arr= null 置空指针
动态空间开辟的是堆,静态空间是栈空间
// TODO ========================== 下面代码对我们上节课 遗留点 完成 末尾工作了
if (new_arr) { // new_arr != NULL 进去if, 重新开辟的堆空间是成功的
free(new_arr);
new_arr = NULL;
arr = NULL; // 他还在指向那块空间,为了不出现悬空指针,指向NULL的空间
} else { // 重新开辟的堆空间是失败的
free(arr);
arr = NULL;
}
// C/C++编译器 数组作为参数传递,会把数组优化成指针(为了高效率)
void getLen(int * resultLen, int intarr[]) { // 模仿了 strLen函数
// TODO 拷贝构造函数的注意点:
// Student stu1("李鬼", 34);
// Student stu2;
// stu2 = stu1; // 这样赋值是不会调用 自定义拷贝构造函数,但是会调用默认赋值
// Student stu2 = stu1; // 这样赋值是会调用 自定义拷贝构造函数,我们自己赋值
C++静态
//已经编译不成功,不 允许这样初始化
static int id = 9;
// 先声明
static int id;
// 再实现
int Dog::id = 9;
- 静态的总结:
- 1.可以直接通过类名::静态成员(字段/函数)
- 2.静态的属性必须要初始化,然后再实现(规则)
- 3.静态的函数只能取操作静态的属性和方法(Java)
3、C++对象中,为什么需要this
// main函数弹栈会 隐士代码:(栈区:delete student …, 堆区需要自己手动delete)
class Student {
private:
char *name;
int age;
public:
static int id; // 先声明
1、分为多个区域,生成代码时会进入栈区,代码会进入代码区域
2、静态类型id,会进入静态区,当任意一个student修改时,全部的id值都会修改
3、public: // 默认的构造函数 栈区开辟空间 暴露 地址 == this指针 (和Java一致的思路)
4、const 修饰函数的this意义何在
class Worker {
public:
char * name;
int age = NULL; // C++中不像Java,Java有默认值, 如果你不给默认值,那么就是系统值 -64664
// int * const 指针常量 指针常量【地址对应的值能改,地址不可以修改】
// const int * 常量指针 常量指针【地址可以修改,地址对应的值不能改】
// 纠结:原理:为什么可以修改age
// 默认持有隐士的this【类型 * const this】
// 类型 * const 指针常量:代表指针地址不能被修改,但是指针地址的值是可以修改的
void change1() {
// 代表指针地址不能被修改
// this = 0x6546; // 编译不通过,地址不能被修改,因为是指针常量
// 地址不可以修改
// this = 0x43563;
// 隐士的this
// 但是指针地址的值是可以修改的
// 地址对应的值能改
this->age = 100;
this->name = "JJJ";
}
// 默认现在:this 等价于 const Student * const //常量指针常量(地址不能改,地址对应的值不能改)
// 原理:修改隐士代码 const 类型 * const 常量指针常量
void changeAction() const {
// 地址不能改
// this = 0x43563;
// 地址对应的值不能改
// this->age = 100;
}
友元函数
// 定义友元函数 (声明,没有实现)
friend void updateAge(Person * person, int age);
重载
class Derry {
private:
int x,y;
// 系统是这样写的 常量引用:不允许修改,只读模式
// const 关键字的解释
// & 性能的提高,如果没有& 运行+ 构建新的副本,会浪费性能
// 如果增加了& 引用是给这块内存空间取一个别名而已,还是同一个地址
Derry operator + (const Derry& derry1) {
// this指针 指向当前对象,所以只需要一个
int x = this->x + derry1.x; // 我在类的里面,是可以拿私有成员的
int y = this->y + derry1.y; // 我在类的里面,是可以拿私有成员的
return Derry(x, y);
}
// 运算符- 重载
Derry operator - (const Derry & derry1) {
int x = this->x - derry1.x;
int y = this->y - derry1.y;
return Derry(x, y);
}
// 对象++ 运算符 重载
void operator ++() { // ++对象
this->x = this->x + 1;
this->y = this->y + 1;
}
void operator ++ (int) { // 对象++
this->x = this->x + 1;
this->y = this->y + 1;
}
// istream 输入 系统的
// ostream 输出 系统的
// 输出 运算符重载 复杂 涉及到规则 重载<<
friend void operator << (ostream & _START, Derry derry1) {
// 输出换行:<< endl;
_START << " 单 哥开始输出了 " << derry1.x << " ! " << derry1.y << " 哥结束了 " << endl;
}
// 多个的 ostream 输出 系统的
// 输出 运算符重载 复杂 涉及到规则 重载 >>
/*friend ostream & operator >> (ostream & _START, const Derry & derry1) {
_START << " 多 哥开始输出了 " << derry1.x << " ! " << derry1.y << " 哥结束了 " << endl;
return _START;
// RxJava 链式调用 .操作符.操作符.操作符 你每次都是放回this 一个思路
}*/
// istream 输入 系统的
friend istream & operator >> (istream & _START, Derry & derry) {
// 接收用户的输入,把输入的信息,给x
// _START >> derry.x;
// _START >> derry.y;
// 可读性不好,简化了
_START >> derry.x >> derry.y;
return _START;
}
};
深拷贝和浅拷原理
浅拷贝
class Student
{
public:
int age;
char * name;
Student() { cout << "空参数构造函数" << endl; }
Student(char * name) :Student(name, 99) {
cout << "一个参数构造函数 this:" << (int)this << endl;
}
Student(char * name, int age) {
cout << "二个参数构造函数 this:" << (int)this << endl;
this->name = (char *)malloc(sizeof(char *)* 10);
strcpy(this->name, name);
this->age = age;
}
~Student() {
cout << "析构函数执行 &this->name:" << (int)this->name << endl;
free(this->name);
this->name = NULL;
}
// 默认有一个拷贝构造函数 隐士的 我们看不见
// 一旦复写了拷贝构造函数,默认的还在吗? Java的构造函数一个思路
// 自定义拷贝构造函数 如果有堆成员,必须采用深拷贝
Student(const Student & stu) {
// stu 旧地址
// this 新地址
// s2 = 新地址
cout << "拷贝构造函数 &stu:" << (int)&stu << " this:" << (int)this << endl;
// 【浅拷贝】:新地址name 旧地址name 指向同一个空间,会造成,重复free的问题,引发奔溃
// 新地址name = 旧地址 (浅拷贝)
// this->name = stu.name;
// 【深拷贝】
this->name = (char *)malloc(sizeof(char *)* 10);
strcpy(this->name, name);
this->age = stu.age;
cout << "拷贝构造函数2 this->name:" << ((int) this->name) << " stu.name:" << (int)stu.name << endl;
// 深拷贝 后面见 原理全部打通的时候讲
} // 此拷贝构造函数执行完 旧会出现一个 this==新地址 给 main函数的 stu
// 默认的拷贝构造函数 是浅拷贝
};
void showStudent(Student stu) {
cout << "showStudent函数:" << (int)&stu << " " << stu.name << "," << stu.age << endl;
}
void main() {
Student stu("刘奋", 31);
cout << "main函数:" << (int)&stu << " " << stu.name << "," << stu.age << endl;
showStudent(stu); // 弹栈后 新地址name释放一遍
// showStudent(stu); // 弹栈后 新地址name释放一遍
// 两次释放新地址name 会奔溃
// 释放一次新地址name 再释放一次旧name也报错
//浅拷贝多次执行会报错,有
showStudent(stu);
showStudent(stu);
showStudent(stu);
// showStudent(stu);
getchar();
} // main函数弹栈 stu 旧地址
// 专业技能:1.研究过C++语言深拷贝原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNwnPFKX-1690813594915)(https://note.youdao.com/yws/res/3344/WEBRESOURCE70978b000481820b5014a2e91403619b)]
1、浅拷贝,每次拷贝会生成新的地址,但新旧地址都指向同一个内存空间,所以释放时如果重复执行就会报错
2、深拷贝:每次都会开辟新的内存空间