1.c++ 引用
其实就是四驱模型的拷贝,引用其实是地址赋值,可以看成同一块内存的另外一个变量
以两个变量值交换为例
#include<stdio.h>
void swap(int &number1, int &number2) {
// &number 此处相当于同一块内存 取了另一个别名
int temp = number1;
number1 = number2;
number2 = temp;
}
void main() {
int number1 = 10;
int number2 = 20;
swap(number1, number2);
printf("%d %d\n", number1, number2); // 结果 20 10
getchar();
}
2.常量指针和指针常量
常量变量:被常量修饰的变量,不能再次被赋值 (Java)
常量指针:const 在 * 之前,指针的地址是可以被再次赋值的(可以修改的),指针地址上面的值(变量)是不能被修改的,常量指针的常量是不能被改变的。
指针常量:const 在 * 之后,指针的地址是不可以被再次赋值的(不可以修改的),指针地址上面的值(变量)能被修改的,指针常量的指针地址是不能被改变的。
// 常量,不能去修改
const int number = 100;
// number = 200; // error
int number1 = 100;
int number2 = 200;
// 常量指针
// int const * n_p = &number2;
// n_p = &number1;
// printf("n_p = %p",n_p); // 地址是可以重新被赋值的
// *n_p = 300; // 值是不能改的 error
// 指针常量
int * const n_p = &number2;
// n_p = &number1; // 地址是不能被重新赋值
*n_p = 300;
printf("number2 = %d", number2);// 值可以被修改,结果:300
3.malloc,free,new,delete 区别
malloc/free 一对,new/delete 是一对
malloc/free 不会去调用构造函数和析构函数
new/delete 会去调用构造函数和析构函数
如果用了new,一定要记得 delete 释放内存
4.构造函数、析构函数和拷贝构造函数
直接上代码说明
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Student
{
public:
// 构造函数
Student(){// 空参数构造函数
cout << "空参数构造函数"<< endl;
}
// Student(char* name):age(0){// 一个参数构造函数, 相当于 this->age = 0
// cout << "一个参数构造函数" << endl;
// this->name = name;
// }
Student(char* name) :Student(name,0){
// 调用两个参数的构造函数
// 注意:先会调用两个参数的构造函数,然后才会执行当前构造函数
cout << "一个参数构造函数" << endl;
}
Student(char* name, int age){// 两个参数构造函数
cout << "两个参数构造函数" << endl;
this->name = (char*)malloc(sizeof(char)*100);
strcpy(this->name,name);
this->age = age;
}
// 2. 析构函数,如果有在对象内部开辟堆内存,可以在析构函数中释放内存
~Student(){
cout << "析构函数" << endl;
// 对象被回收的时候会被调用
// 释放内存
free(this->name);
this->name = NULL;
}
/*
4.拷贝构造函数,在 java 中如果 Student stu2 = stu1,那么我们一般会认为 stu2 对象和 stu1 对象是同一个对象,
但是在 c++ 中这种认为就是错误的,我们可以分别打印 stu1 和 stu2 的地址发现并不相等,
所以 stu1 和 stu2 并不是同一个对象,而是会调用拷贝构造函数。*/
// 对象会有一个默认的拷贝构造函数,用来对象之间的赋值
Student(const Student& stu){// 常量的引用
cout << "拷贝构造函数" << endl;
// this->name = stu.name;// 浅拷贝
// 如果动态开辟内存,一定要采用深拷贝
this->name = (char*)malloc(sizeof(char)* 100);
strcpy(this->name, stu.name);
this->age = stu.age;
}
Student getStudent(char* name){
Student stu(name);// 栈 ,方法执行完,这个对象会被回收,但是发现调用了拷贝构造函数
cout << &stu << endl;
return stu;// 会返回一个新的 Student 对象,而栈内开辟的 stu 是会被回收
}
void printStudent(Student stu){// stu 是该方法栈中一个新的对象,拷贝构造函数赋值,方法执行完会调用析构函数
cout << stu.getName() << " , " << stu.getAge() << endl;
}
void main(){
// 1. = 会调用拷贝构造函数
// Student stu1("Darren", 24);
// Student stu2 = stu1; // = 是赋值,把里面所有定义的属性赋值,c/c++ 编译器帮我们做的,其实会调用对象的拷贝构造
// Student stu2;// 声明变量,开辟变量内存
// stu2 = stu1; // 这个不会去调用拷贝构造函数,但是会赋值 c 的类似
// 2. 第二种场景 作为参数返回的时候会调用拷贝构造函数
// Student stu = getStudent("Jack");
// cout << &stu << endl;
// 3. 第三种场景 作为参数传递的时候会调用拷贝构造函数
// Student stu("Darren", 24);
// printStudent(stu);
// 知识的补充
Student stu = getStudent("Jack");
// cout << stu.getName() << " , " << stu.getAge() << endl;
printStudent(stu);
getchar();
}
private:
int age;
char* name;
public:
int getAge(){
return this->age;
}
char* getName(){
return this->name;
}
void setAge(int age){
this->age = age;
}
void setName(char* name){
this->name = name;
}
};
5.内存四驱模型
在 c/c++ 中我们将运行时数据,分为四个区域分别是:栈区,堆区,数据区,代码区。我们详细来介绍下:
- 栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
- 堆区:一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收。
- 数据区:存放全局变量、静态变量和常量字符串等等。 程序结束后由系统释放。
- 存放函数体的二进制代码。
6.可变参数
#include <iostream>
#include <stdarg.h>
using namespace std;
int sum(int count, ...) { // 格式:count代表参数个数, ...代表n个参数
va_list ap; // 声明一个va_list变量
va_start(ap, count); // 第二个参数表示形参的个数
int sum = 0;
for (int i = 0; i < count; i++) {
sum += va_arg(ap, int); // 第二个参数表示形参类型
}
va_end(ap); // 用于清理
return sum;
}
void main(){
int sumValue = sum(3,8,10,9);
cout << sumValue << endl; // 输出结果:27
getchar();
}
步骤:
定义头文件 stdarg.h
定义参数:参数格式 (int count, ...)
函数定义中创建一个 va_list 变量
初始化: va_start(ap, count);
循环将各个参数进行相加
清理:调用清理 va_end(va_list); 清理内容,最后调用
7.static 关键字
- 静态属性在 c++ 中必须要初始化
class Test {
public static int tag;
}
int test = Test::tag = 1; // 或在定义 tag 的时候赋值初始化
8.C++程序在内存中的分布
- 栈(Stack):局部变量,函数参数等存储在该区,由编译器自动分配和释放.栈属于计算机系统的数据结构,进栈出栈有相应的计算机指令支持,而且分配专门的寄存器存储栈的地址,效率很高,内存空间是连续的,但栈的内存空间有限。
- 堆(Heap):需要程序员手动分配和释放(malloc,free),属于动态分配方式。内存空间几乎没有限制,内存空间不连续,因此会产生内存碎片。操作系统有一个记录空间内存的链表,当收到内存申请时遍历链表,找到第一个空间大于申请空间的堆节点,将该节点分配给程序,并将该节点从链表中删除。一般,系统会在该内存空间的首地址处记录本次分配的内存大小,用于 free 释放该内存空间。
- 全局/静态存储区:全局变量,静态变量分配到该区,到程序结束时自动释放,包括 DATA 段(全局初始化段)与 BBS 段(全局未初始化段)。其中,初始化的全局变量和静态变量存放在 DATA 段,未初始化的全局变量和静态变量存放在 BBS 段。BBS 段特点:在程序执行前 BBS 段自动清零,所以未初始化的全局变量和静态变量在程序执行前已经成为 0.
- 文字常量区:存放常量,而且不允许修改。程序结束后由系统释放。
- 程序代码区:存放程序的二进制代码