基于C语言的C++学习(一)
这是作者的学习经历和学习笔记,如有错误,欢迎指正。
学习网址是黑马程序员的C++
const和指针
const修饰指针–常量指针
指针指向的地址可以改变,指针指向的地址里面的值不可以改
int a = 10;
int b = 20;
const int * p = &a; //常量指针
*p = 30; //错误
p = $b; //正确
const修饰常量–指针常量
指针指向的地址不可以改,指针指向的地址的值可以改
int a = 10;
int b = 20;
int * const p = &a; //指针常量
p = &b; //错误
*p = 30; //正确
const既修饰指针,又修饰常量
指针指向的地址和地址里面的值都不能改
int a = 10;
int b = 20;
const int * const p = &a;
p = &b; //错误
*p = 30; //错误
C++核心编程
内存区分模型
- 代码区:存放函数体的二进制代码,由操作系统进行管理
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
程序运行前
程序编译后,生成exe可执行文件,未执行该程序前分为两个区域
- 代码区
- 存放cpu执行的机器指令
- 代码区是共享的,共享的目的是对于频繁执行的程序,只需要在内存中有一份代码即可
- 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
- 全局区
- 全局变量和静态变量存放于此
- 全局区还包含了常量区,字符串常量和其他常量
- 该区域的数据在程序结束后由操作系统释放
程序运行后
- 栈区
- 由编译器自动分配释放,存放参数的参数值,局部变量等
- 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
- 堆区
- 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
- 在C++中利用new关键字在堆区开辟内存
new运算符
new的基本用法
-
作用
-
分配基本数据类型的内存
-
分配对象的内存
-
分配数组的内存(需用
delete[]释放)
-
-
语法
new 数据类型
#include<iostream>
using namespace std;
int * func()
{
int * p = new int(10);
return p;
}
void test01()
{
int * p = func();
cout << *p << endl;
cout << *p << endl;
delete p;
cout << *p << endl; //会报错
}
int * func02()
{
int * p = new int[10];
return p;
}
void test02()
{
int * arr = func();
for(int i = 0; i < 10; i++)
{
arr[i] = i;
}
for(int i = 0; i < 10; i++0)
{
cout << arr[i] << endl;
}
delete[] arr;
cout << arr << endl; //会报错
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
- 基本数据类型分配:
new int分配一个int类型的内存空间,返回指向该空间的指针,使用delete释放。 - 对象分配:
new Student("张三", 20)创建Student类的实例,会自动调用构造函数初始化对象,使用delete释放时会调用析构函数。 - 数组分配:
new int[5]分配能存储 5 个int的数组,注意释放时必须使用delete[]而不是delete,否则可能导致内存泄漏。
注意事项
- 使用
new分配的内存位于堆(heap)中,而不是栈(stack)中 - 必须成对使用
new/delete和new[]/delete[],否则会导致内存泄漏 - 可以使用
nullptr检查指针是否有效(如if (pInt != nullptr)) - C++11 后推荐使用智能指针(如
unique_ptr、shared_ptr)来自动管理内存,减少手动释放的风险
引用
引用的基本使用
- 作用:给变量起别名
- 语法:
数据类型 &别名 = 原名
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int &b = a;
cout << a << endl;
cout << b << endl;
cout << &a << endl;
cout << &b << endl;
system("pause");
return 0;
}
注意事项
- 引用必须初始化
- 引用一旦初始化后就不能更改
扩展
引用做函数参数
- 作用:函数传参时,可以利用引用的技术让形参修饰实参
- 优点:简化指针的使用
#include<iostream>
using namespace std;
void myswap01(int * a, int * b) //地址传递
{
int temp = *a;
*a = *b;
*b = temp;
}
void myswap02(int &a, int &b) //引用传递
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
myswap01(&a, &b);
myswap02(a, b);
cout << a << endl;
cout << b << endl;
system("pause");
return 0;
}
引用做函数返回值
- 作用:引用可以作为函数的返回值存在
- 注意:不要返回局部变量引用
- 用法:函数调用可以作为左值
#include<iostream>
using namespace std;
int& test01()
{
int a = 10;
return a;
}
int& test02()
{
static int a = 10;
return a;
}
int main()
{
int &ret = test01();
cout << ret << endl;
cout << ret << endl; //两次输出的值会不同
int &ret02 = test02();
cout << ret02 << endl;
cout << ret02 << endl; //两次输出的值相同
test022() = 1000;
cout << ret02 << endl;
cout << ret02 << endl; //两次输出的值相同,为1000
}
引用的本质
本质:引用的本质在C++中是一个常量指针
int& ref = a等于int* const ref = aref = 20等于*ref = 20
常量引用
作用:
- 常量引用主要用来修饰形参,防止误操作
- 允许传入常量或临时值
#include<iostream>
using namespace std;
void showvalue(const int & val)
{
val = 1000; //会报错,不能修改
cout << "val = " << val << endl;
}
int main()
{
int a = 10;
showvalue(a);
cout << "a = " << a << endl;
system("pause");
return 0;
}
非 const 引用只能接收可修改的左值(如变量),而 const 引用可以接收:
- 变量(左值)
- 常量(如
5、3.14等右值) - 表达式的临时结果(如
a + b的计算结果)
这极大地扩展了函数的适用范围:
void print(const int& x) {
cout << x << endl;
}
int main() {
int a = 10;
print(a); // 允许:变量(左值)
print(20); // 允许:常量(右值)
print(a + 30); // 允许:表达式临时结果(右值)
return 0;
}
如果没有 const,上面后两种调用会编译报错。
函数高级
函数的默认参数
- 函数可以在定义中写入形参的默认值,在后续的函数调用中若没有写入实参,则使默认值传入
- 如果某个参数有了默认值,那么从左往右之后的参数都因该有默认值,即有默认值的参数放到右边
- 函数的声明和定义不能同时有默认值
#include<iostream>
#include<string>
using namespace std;
int func(int a, int b = 20, int c = 30)
{
return a + b + c;
}
int func02(int a = 20, int b, int c = 30) //报错
{
return a + b + c;
}
int func03(int a, int b = 20, int c = 30);
int func03(int a, int b = 20, int c = 30) //报错
{
return a + b + c;
}
int main()
{
cout << func(10) << endl; //输出60
cout << func(10, 40) << endl; //输出80
system("pause");
return 0;
}
函数的占位参数
- 作用:函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
- 语法:
返回值类型 函数名 (数据类型){}
#inc;ude<iostream>
using namespace std;
void func(int a, int)
{
cout << "this is func" << endl;
}
void func02(int a, int = 10)
{
cout << "this is func" << endl;
}
int main()
{
func(10, 10);
system("pause");
return 0;
}
函数重载
- 作用:函数名可以相同,提高复用性
- 函数重载满足条件:
- 同一个作用域下
- 函数名称相同
- 函数参数类型不同或者个数不同或者顺序不同
- 注意:函数的返回值不可以作为函数重载的条件
#include<iostream>
using namespace std;
void func()
{
cout << "func的调用" << endl;
}
void func(int a)
{
cout << "func(int a)的调用" << endl;
}
void func(double a)
{
cout << "func(double a)的调用" << endl;
}
void func(int a, double b)
{
cout << "func(int a, double b)的调用" << endl;
}
void func(double a, int b)
{
cout << "func(double a, int b)的调用" << endl;
}
int main()
{
func();
func(10);
func(10.0);
func(10, 10.0);
func(10.0, 10);
system("pause");
return 0;
}
函数重载的注意事项
- 引用作为重载条件
#include<iostream>
#include<string>
using namespace std;
void func(int &a)
{
cout << "func(int &a)的调用" << endl;
}
void func(const int &a)
{
cout << "func(const int &a)的调用" << endl;
}
int main()
{
int a = 10;
func(a); //输出:func(int &a)的调用
func(10); //输出:func(const int &a)的调用
system("pause");
return 0;
}
- 函数重载碰到默认参数
#include<iostream>
#include<string>
using namespace std;
void func(int a, int b = 10)
{
cout << "func(int a, int b = 10)的调用" << endl;
}
void func(int a)
{
cout << "func(int a)的调用" << endl;
}
int main()
{
int a = 10;
func(10); //报错
func(10, 10); //输出:func(int a, int b = 10)的调用
system("pause");
return 0;
}
函数模板
函数模板(Function Template)是 C++ 中实现泛型编程的核心机制,它允许你定义一个通用的函数框架,支持多种数据类型,而无需为每种类型重复编写相同逻辑的代码。
基本语法
函数模板的定义格式如下:
template <typename T> // 模板参数声明,T是类型占位符
返回类型 函数名(参数列表) {
// 函数体(使用T作为类型)
}
其中 typename 也可以替换为 class(两者在函数模板中含义相同)。
简单示例:通用交换函数
#include <iostream>
using namespace std;
// 函数模板:交换任意类型的两个变量
template <typename T> // 声明模板参数T
void swapValues(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
// 交换整数
int x = 10, y = 20;
swapValues(x, y);
cout << "交换后:x=" << x << ", y=" << y << endl; // 输出:x=20, y=10
// 交换浮点数
double f1 = 3.14, f2 = 6.28;
swapValues(f1, f2);
cout << "交换后:f1=" << f1 << ", f2=" << f2 << endl; // 输出:f1=6.28, f2=3.14
// 交换字符
char c1 = 'A', c2 = 'B';
swapValues(c1, c2);
cout << "交换后:c1=" << c1 << ", c2=" << c2 << endl; // 输出:c1=B, c2=A
return 0;
}
函数模板的工作原理:
-
模板定义:编译器不会直接编译模板本身,而是将其视为 “代码生成器”。
-
实例化
:当你用具体类型(如
int、double)调用模板函数时,编译器会根据类型自动生成对应版本的函数(称为模板实例)。- 例如调用
swapValues(x, y)(x、y是int)时,编译器会生成一个参数为int&的swapValues函数。
- 例如调用
-
类型检查:编译器会确保模板中的操作对实际类型是合法的(如
T类型必须支持赋值操作)。
模板的显式实例化与特化
-
显式实例化:手动指定模板类型,强制编译器生成特定版本的函数:
-
// 显式实例化int类型的swapValues template void swapValues<int>(int& a, int& b); -
模板特化:为特定类型提供自定义实现(当通用模板不适用时):
#include <iostream> #include <cstring> // 用于字符串比较函数strcmp using namespace std; // 通用模板:比较两个值的大小并返回较大者 template <typename T> T maxValue(T a, T b) { cout << "使用通用模板比较:"; return (a > b) ? a : b; } // 模板特化:专门处理const char*(字符串)类型 template <> const char* maxValue<const char*>(const char* a, const char* b) { cout << "使用字符串特化模板比较:"; // 字符串比较不能直接用>,需要用strcmp int result = strcmp(a, b); return (result > 0) ? a : b; } int main() { // 1. 比较整数(使用通用模板) int num1 = 10, num2 = 20; cout << maxValue(num1, num2) << endl; // 2. 比较浮点数(使用通用模板) double f1 = 3.14, f2 = 2.71; cout << maxValue(f1, f2) << endl; // 3. 比较字符串(使用特化模板) const char* str1 = "apple"; const char* str2 = "banana"; cout << maxValue(str1, str2) << endl; return 0; }
注意事项
- 模板定义通常放在头文件:因为编译器需要在调用处看到完整的模板定义才能实例化。
- 类型限制:模板中的操作必须对实际类型有效(如
+操作对int有效,但对自定义对象可能需要重载+)。 - 不允许自动类型转换:模板参数的类型必须严格匹配(除非使用显式指定类型)。
- 避免过度泛化:只在逻辑完全通用时使用模板,否则可能导致代码复杂。
函数模板是 C++ 泛型编程的基础,广泛用于标准库(如 std::vector、std::sort 等),它能大幅减少重复代码,提高代码复用性和灵活性。
&spm=1001.2101.3001.5002&articleId=153109183&d=1&t=3&u=a4e5c3f1e8a047798b6dfea63aa51dfd)
265

被折叠的 条评论
为什么被折叠?



