1.C++相较于 C 语言的增强
1.1引用 Reference
1.1.1 引用基础案例
&
定义了一个 int 类型的引用,引用名称为 ret ,使用 num 赋值当前引用,可以认为
1.ret 是一个引用
2.ret 是 num 变量的别名
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int num = 10;
/*
C++ 特定的数据形式【引用】需要使用的【标记】标点符号
&
定义了一个 int 类型的引用,引用名称为 ret ,使用 num 赋值当前引用
可以认为
1.ret 是一个引用
2.ret 是 num 变量的别名
*/
int &ret = num;
cout << "num = " << num << endl;
cout << "ret = " << ret << endl;
cout << "-----------------------" << endl;
num = 20;
cout << "num = " << num << endl;
cout << "ret = " << ret << endl;
cout << "-----------------------" << endl;
ret = 1000;
cout << "num = " << num << endl;
cout << "ret = " << ret << endl;
return 0;
}
1.1.2一个变量可以有多个引用
一个变量可以同时有多个引用,就例如一个人可以有多个外号
定义了一个 int 类型的引用,引用名称为 ret ,使用 num 赋值当前引用。
ret1 ret2 ret3 都是 num 的引用。
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int num = 200;
int & ref1 = num;
int & ref2 = num;
int & ref3 = num;
cout << "num : " << num << endl;
cout << "ref1 : " << ref1 << endl;
cout << "ref2 : " << ref2 << endl;
cout << "ref3 : " << ref3 << endl;
cout << "-------------" << endl;
ref3 = 1000;
cout << "num : " << num << endl;
cout << "ref1 : " << ref1 << endl;
cout << "ref2 : " << ref2 << endl;
cout << "ref3 : " << ref3 << endl;
return 0;
}
1.1.3引用在函数参数中的使用
引用在函数参数中使用,相当于地址传递
优势
1.相较于指针方式节省空间,引用不占用额外的内存空间,指针变量占内存的 8 个字节(64位操作系统)
2.引用不会有额外的运算符使用,操作简单,使用方便
3.引用效率高于指针。
#include <iostream>
using namespace std;
// 值传递
void swap1(int n1, int n2);
// 地址传递
void swap2(int *p1, int *p2);
/*
引用传递
当前函数所需的参数类型是引用类型
@param r1 int 类型引用
@param r2 int 类型引用
*/
void swap3(int &r1, int &r2);
int main(int argc, char const *argv[])
{
int num1 = 200;
int num2 = 1000;
swap1(num1, num2);
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
cout << "------------------" << endl;
swap2(&num1, &num2);
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
cout << "------------------" << endl;
/*
引用的优势
1,相较于指针方式省空间,引用不占用额外的内存空间
指针变量占用内存 8 个字节(64位系统)
2,引用不会有额外运算符使用,操作简单,使用方便
3,引用效率高于指针、
swap3(num1,num2);
参数赋值操作相当于
int & r1 = num;
int & r2 = num2;
*/
swap3(num1, num2);
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
cout << "------------------" << endl;
return 0;
}
void swap1(int n1, int n2)
{
int temp = n1;
n1 = n2;
n2 = temp;
}
void swap2(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void swap3(int &r1, int &r2)
{
int temp = r1;
r1 = r2;
r2 = temp;
}
1.1.4指针引用
可以降低代码的逻辑复杂度,提升代码效率,同时节约内存
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
struct Student
{
int id;
char name[32];
int age;
};
/*
当前函数所需参数为二级指针,需要修改外部指针变量存储的数据内容
如果提供的参数类型为一级指针,有且只能修改当前指针指向的数据空间
内容,无法修改当前变量存储数据情况,提供二级指针,可以修改当前指针
变量存储的数据内容
*/
void alloc_new_memory(Student ** stu);
/*
当前参数所需参数为【Student 类型指针引用】,可以直接传入 Student *
指针类型
降低代码的逻辑复杂度,提升代码效率,同时节约内存
*/
void alloc_new_memory_by_reference(Student *& stu);
int main(int argc, char const *argv[])
{
Student *stu = (Student *)malloc(sizeof(Student));
memset(stu,0,sizeof(Student));
cout << "stu : " << stu << endl;
alloc_new_memory(&stu);
cout << "stu : " << stu << endl;
alloc_new_memory_by_reference(stu);
cout << "stu : " << stu << endl;
free(stu);
return 0;
}
void alloc_new_memory(Student **stu)
{
free(*stu);
int * arr = (int *)malloc(sizeof(int) * 10);
*stu = (Student *)malloc(sizeof(Student));
memset(*stu,0,sizeof(Student));
free(arr);
}
void alloc_new_memory_by_reference(Student * & stu)
{
free(stu);
int * arr = (int *)malloc(sizeof(int) * 10);
stu = (Student *)malloc(sizeof(Student));
memset(stu,0,sizeof(Student));
free(arr);
}
1.2const 关键字
1.1.1基本特征
基本变量 const 修饰
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
const int num = 10;
cout << "num : " << num << endl;
num = 12;// 会报错
/*
error: assignment of read-only variable ‘num’
const 修饰当前变量,可以简单认为当前变量为常量
后续无法进行赋值操作
当前 num 只读 【read-only】
*/
return 0;
}
const int*p
const 修饰限制指针指向数据内容空间
p 存储的地址可以改变,p 指向地址的数据内容不可修改
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
const int num = 10;
cout << "num : " << num << endl;
const int *p = #
const int num1 = 100;
/*
*p = 200; ERROR 表达式必须是可修改的左值C/C++
*p ==> num 而 num 是 read-only 只读变量
p = # const int * p 指针指向的数据内容空间
不可以修改,但是存储的地址可以修改。
*/
p = &num1;
return 0;
}
const int * const p
const 修饰限制指针指向数据内容空间,同时 const 修饰限制指针变量数据存储地址
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int num1 = 100;
const int num = 10;
const int * const p1 = #
/*
*p1 = 200;
*p1 assignment of read-only location ‘*(const int*)p1’
指向空间数据内容无法修改,是一个常量
p1 = &num1;
p1 error: assignment of read-only variable ‘p1’
p1 也是一个常量,指针常量存储地址不可以修改
*/
return 0;
}
int * const p
const 修饰限制指针变量数据存储
int num2 = 200;
int * const p2 = &num2;
int num3 = 100;
/*
p2 指向数据空间内容可以修改,但是存储的地址不能修改
*/
*p2 = 1000;// √
p2 = &num3; // ×
1.2.2 使用 const 替代 #default 无参数宏
宏只可以做简单的替换,不会考虑
1.数据类型问题
2.语法出错问题const 修饰变量作为常量使用,具有的优点
1.具备数据类型特征,可以检查数据类型情况
2.有一定的语法限制,可以前置错误情况
3.使用全局变量形式完成 const 常量限制,功能性和宏类似,安全性更好
#include <iostream>
#define COUNT 10
const int num = 10;
using namespace std;
int main(int argc, char const *argv[])
{
cout << "count : " << COUNT << endl;
cout << "num : " << num << endl;
return 0;
}
1.3 new 和 delete
1.3.1案例
C++ 用于动态分配内存的关键字,效果和 C 语言 malloc calloc realloc free 一致
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
int main(int argc, char const *argv[])
{
/*
C 语言申请内存方式和释放方式
*/
int *arr = (int *)malloc(sizeof(int) * 10);
memset(arr, 0, sizeof(int) * 10);
free(arr);
arr = NULL;
/*
C++ 申请内存方式和释放方式
*/
int *arr2 = new int[10];
memset(arr2, 0, sizeof(int) * 10);
// 使用
for (int i = 0; i < 10; i++)
{
arr2[i] = i * 2;
}
for (int i = 0; i < 10; i++)
{
cout << "arr2[" << i << "] = " << arr2[i] << endl;
}
delete arr2;
arr2 = NULL;
return 0;
}
1.3.2 结构体空间内容操作
#include <iostream>
using namespace std;
struct Student
{
int id;
string name;
int age;
};
int main(int argc, char const *argv[])
{
/*
new 申请内存【堆区】空间,创建一个可以存储 Student 数据内存空间
返回对应空间首地址
*/
Student *stu = new Student;
stu->id = 10;
stu->name = "小绿茶z";
stu->age = 18;
cout << "ID : " << stu->id << endl;
cout << "Name : " << stu->name << endl;
cout << "Age = " << stu->age << endl;
delete stu;
stu = NULL;
return 0;
}
1.4 内联函数
inline 关键字修饰
在C++中,
inline
关键字用于提示编译器将函数的代码内联展开,而不是通过函数调用的方式执行。对于inline
函数,通常在头文件中提供函数的定义,而不是在源文件中。这让编译器能够在每个调用点直接插入函数的代码,从而提高执行效率。虽然对于
inline
函数不要求显式的函数声明,但在一些情况下,如果你想在不同的源文件中使用inline
函数,最好在这些源文件中都提供相同的函数声明,以确保一致性。inline 函数可以提升一定的开发效率,在代码执行前 inline 函数会替代调用位置内容,替代之前会检查以下内容
1.函数名
2.参数数据类型
3.整个 inline 函数结果是一个整体
#include <iostream>
using namespace std;
/*
有参宏,可以完成一定的替换代码内容,但是有以下内容
1.没有严格的数据类型的限制,没有数据类型检查
2.替换结果会因为宏内容导致结果不一致,无法满足程序期望
*/
#define ADD(a,b) a + b
/*
首先这里是一个函数,并且时有参数有返回值函数
函数声明有一个关键字修饰 inline
*/
inline int add(int a,int b)
{
return a + b;
}
int main(int argc, char const *argv[])
{
int ret = 0;
ret = ADD(10,20);
cout << ret << endl;
ret = add(10,20);
cout << ret << endl;
return 0;
}
1.5函数参数补充说明
1.5.1关于无参数函数
C 语言中,如果按照严格的代码规范,无参数函数,小括号需要使用 void 占位
void test(void);
C++语言中,不需要用 void 占位
void test();
1.5.2 函数的默认参数
#include <iostream>
using namespace std;
/*
声明位置有一个参数存在默认值
*/
void test(int num1,int num2 = 10);
int main(int argc, char const *argv[])
{
// 如果函参数带有默认值,对应参数可以不提供实际参数
test(100);
// 默认值可以修改
test(200,1000);
return 0;
}
// 函数的实现位置,不需要再次明确函数参数默认值
void test(int num1,int num2)
{
cout << "num1 : " << num1 << endl;
cout << "num2 : " << num2 << endl;
}
1.5.3重载【重点!】
函数的重载
1.在同一个区域范围内,同一个命名空间,同一个文件,同一个类内,函数名称一致,但是函数的形式参数列表,数据类型,数据顺序,数据个数必须不一致。
2.函数的返回值不作为函数的唯一标记【会有语法错误,重复定义】
#include <iostream>
using namespace std;
/*
函数的返回值类型不作为函数的唯一标记,【语法错误】函数重复定义
无法通过函数名(实际参数数据类型)来明确目标函数是哪一个
int test();
【要求】
1.在同一个区域范围内
2,函数名必须要一致
3,函数的形式参数列表必须不一致
*/
void test();
void test(int num);
int main(int argc, char const *argv[])
{
test();
test(10);
return 0;
}
void test()
{
cout << "无参数 test " << endl;
}
void test(int num)
{
cout << "num : " << num << endl;
}
2.动态数组实现
2.1功能概述
整体结构为结构体,内存使用指定类型数组,针对于当前指定的类型,完成对应的【增删改查】
2.2设计整个结构体的基本情况
考虑到数组操作中,容量问题较为复杂,可以在结构体中设计对应的数据,将容量和有效元素个数进行保存
#define DEAFULT_CAPACITY 10
struct MyArray
{
// 存储 Student * 指针数据数组,默认容量为 10
Student *arr[DEAFULT_CAPACITY];
// 整个底层数组的容量
int capacity = DEAFULT_CAPACITY;
// 当前数组中存储的有效元素个数
int size;
};
2.3学生相关数据设计
Student.h 头文件
#ifndef _STUDENT_H_
#define _STUDENT_H_
#include <iostream>
using namespace std;
struct Student
{
int id;
string name;
int age;
void setId(int id);
int getId();
void setName(string name);
string name();
void setAge(int age);
int getAge();
};
student.cpp C++ 文件
#include "student.h"
void Student::setId(int id) { this->id = id; }
int Student::getId() { return id; }
void Student::setName(string name) { this->name = name; }
string Student::getName() { return name; }
void Student::setAge(int age) { this->age = age; }
int Student::getAge() { return age; }
2.4 为什么用 this 指针
在C++中,this 指针是一个指向当前对象的指针,它在类的成员函数中起到指向当前对象的作用。当你使用成员函数去修改或访问成员变量时,你需要使用
this
指针。使用
this->
是因为在成员函数内,this
指针是一个指向当前对象的指针,而成员变量是属于对象的。因此,为了明确指出你想要访问的是对象的成员变量而不是局部变量,使用this->
是一个显式的方式。
void Student::setId(int id) { this->id = id; }
int Student::getId() { return id; }
在这里,
this->id
表示当前对象的id
成员变量,而id
表示该成员函数的参数。使用this->
可以帮助消除潜在的歧义,确保你正在访问对象的成员而不是同名的局部变量。总的来说,使用
this->
是一种良好的编程实践,特别是在成员函数内部,它提高了代码的清晰度和可读性。
用 this 指针的情况:
1.区分局部变量和成员变量: 当成员函数的参数与成员变量同名时,使用this
可以明确指出你正在访问对象的成员变量。
2.在类的方法中返回当前对象: 在链式调用中,你可能希望在一个成员函数中返回当前对象,以便可以继续在同一个对象上调用其他函数。
MyClass& MyClass::increment() {
this->x++;
return *this; // 返回当前对象的引用
}
3.在成员函数内部访问其他成员函数: 在一个成员函数内部,你可以使用this
指针来调用其他成员函数。这是因为成员函数可以直接访问当前对象的成员。
void MyClass::doSomething() {
this->increment(); // 调用当前对象的成员函数
}
通常情况下,如果在成员函数中没有同名的局部变量,你可以选择性地省略this
,因为编译器会自动理解你指的是当前对象。使用this
通常是为了增加代码的清晰性和可读性。在一些简单的情况下,可以选择不使用this
。