c++学习记录
定义一个指针变量p
int* p;
定义一个变量 c ,把c的地址赋值给p指针,&是取地址符号
int c = 10;
//a的地址赋值给p指针
p = &c;
// *p解引用 即取出p内存地址对应的值
*p = 1000;
c++ 中规定在32位系统中指针占用4个字节的空间,在64位系统中占用8个字节的空间
cout << "指针p占用的空间大小为" << sizeof(p) << endl;
测试了下我的电脑是64位输出占用大小为8
定义: 指针变量指向内存中编号位0的空间,16进制表示为0x00
用途: 初始化指针变量
注意: 空指针指向的内存是不能访问的(0-255之间的内存编号是系统占用的不能访问)
初始化p为空指针
int* p = NULL;
指针指向了未申请的内存空间即为野指针,试图操作野指针编译可以通过但程序运行将会报错“读取访问权限冲突”
int* p = (int*)0x1100;
cout << *p << endl;
特点:指针的指向可以修改但是指针指向的值不能修改
下面代码是可以运行的
int a1 = 4;
int a2 = 4;
int a3 = 3;
//常量指针
const int* p = &a1;
p = &a2;
p = &a3;
如果这样写则无法编译,因为修改了指针指向的值
*p = 10;
特点: 指针的指向不可以改,指针指向的值可以改
int* const p;
示例:
// 定义一个数组
int arr[5] = { 1,2,4,5,7 };
//定义一个指针, arr赋值给p指针是吧首地址赋值给p指针
int* p = arr;
//使用指针访问数组
cout << *(p + 1);
//指针遍历数组
for (int i = 0; i < 5; i++)
{
cout << *(p + i);
}
//使用p[0] 也可以访问数组第一个元素等价于*(p+1)
示例2:
//定义一个存int变量地址的指针
int* pArray;
//给pArray 赋值数组的首地址
pArray = new int[100];
//打印的结果是一样的
cout << pArray << endl;
cout << &pArray[0] << endl;
//*(pArray + 1) 等价于 pArray[1]
cout << *(pArray + 1) << endl;
cout << pArray[1] << endl;
struct Student
{
int age;
string name;
}s2;
Student s1;
//方式一
s1.age = 28;
s1.name = "张山";
cout << s1.age << s1.name << endl;
//方式二
s2.age = 34;
s2.name = "李四";
cout << s2.age << s2.name << endl;
//方式三
Student s3 = { 56,"王五" };
cout << s3.age << s3.name << endl;
struct Student
{
int age;
string name;
};
Student sarr[10] =
{
{19,"关羽"},
{20,"张飞"},
};
sarr[1].name = "刘备";
for (int i = 0; i < 2; i++)
{
cout << sarr[i].age << sarr[i].name << endl;
}
struct Student
{
int age;
string name;
};
Student s1 = { 16,"赵云" };
Student* p = &s1;
cout << p->name << p->age << endl;
值传递修改形参后实参不会改变,引用传递修改形参后实参也会改变
struct Student
{
int age;
string name;
};
void Print1(Student s) {
s.age = 100;
cout << s.age;
}
void Print2(Student* p) {
p->age = 200;
cout << p->age;
}
使用 const修饰形参防止函数内部数据被修改
void Print3(const Student* p) {
p->age = 200;(报错)
cout << p->age;
}
c++程序在执行时,将内存大方向划为四个区域
局部变量,形参都存放在栈区,栈区的数据在函数执行完后自动释放
int* Print4() {
int a = 10;
return &a;
}
int* c = Print4();
cout << *c << endl;
cout << *c << endl;
上面函数返回了局部变量的地址这种做法不建议,在vs2022-x86环境下编译第二次打印 *c无法获取到10, 在x64中两次都可以获取10值
在vs2022 x64环境下,由于x64架构的特点,当函数返回后,局部变量所在的栈空间可能还没有被重用,因此指针c
仍然指向之前temp
所在的位置。这意味着即使temp
已经不再有效,c
指向的内存可能还没有被覆盖,因此第一次cout << *c << endl;
仍然能够打印出10。对于第二次打印,如果在这之间没有其他代码执行导致栈空间被重用,那么*c
可能仍然保持原来的值,因此第二次打印也输出10。
在x86架构下,由于栈空间的管理方式不同,局部变量temp
所在的空间可能很快就被重用了。这意味着在Print4()
函数返回后,c
指向的内存区域可能会被新的数据覆盖,因此第二次尝试访问*c
时,你可能得到一个不确定的结果,甚至会导致程序崩溃
由程序员分配释放,若程序员不释放,程序结束时由操作系统的回收
在c++中主要利用new开辟内存
int* func() {
//利用new关键字
//指针的本质是局部变量 ,放在栈上,指针保存的数据放在堆区
int* p = new int(10);
return p;
};
int* p = func();
cout << p << endl;
//打印结果:0096F148
栈 | 堆 |
---|---|
int *p p = 0096F148 | 内存地址 0096F148 值 10 |
栈上的指针变量保存的是值所在堆的内存地址,值保存在堆上,解引用实际是拿指针对应的内存地址区找堆上的内存地址获取其值
c++ 使用new操作符在堆区开辟内存
堆区开辟的数据由程序员手动开辟,手动释放,释放利用操作符delete
语法:new 数据类型
用new创建的数据会返回对应类型的指针
int* func01() {
return new int(10);
};
int* p = func01();
cout << *p << endl;
cout << *p << endl;
delete p;
cout << *p << endl;//调用报错内存已被释放
new 数组
int* arr = new int[10];
//释放数组
delete[] arr;
int q = 100;
//取别名
int& e = q;
cout << q << endl;
cout << e << endl;
e = 40;
cout << q << endl;
cout << e << endl;
输出 100 100 40 40
int q = 100;
//取别名
int& e = q;
//注意事项
int r = 200;
e = r;// 赋值操作没有修改引用
cout << q << endl;
cout << e << endl;
void func02(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
void func03(int* a, int* b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void func04(int& a, int& b) {
int temp;
temp = a;
a = b;
b = temp;
}
int q = 10;
int w = 30;
//值传递
func02(q, w);
cout << q << endl;
cout << w << endl;
//指针传递
func03(&q, &w);
cout << q << endl;
cout << w << endl;
//引用传递
func04(q, w);
cout << q << endl;
cout << w << endl;
输出:
10
30
30
10
10
30
//错误的不能返回局部变量的引用
int& func06() {
int a = 10;
return a;
}
int& ref = func06();
cout << ref << endl;
cout << ref << endl;
输出:
10
2082318576
func06() = 1000;
引用的本质是一个指针常量
指针常量: 指针的指向不可以改,指针指向的值可以改
int a = 10;
//自动转换int* const ref = &a
int& ref = a;
//内部发现ref是引用,自动帮我们转换为:*ref= 20;
ref = 20;
主要用来修饰形参,防止误操作
声明和实现只能一个有默认参数
void func(int a,int)
{
}
运算符重载概念:对已有的运算符重新定义,赋予其另一种功能,以适应不同的数据类型
双目算数运算符: + ,-,*,/,%(取模)
逻辑运算符:||,&&,!
单目运算符:+,-,&(取地址)
关系运算符:==,!=,<,>,<=,>=
自增自减运算符:++,--
位运算符:|(按位或),&(按位与),~(按位取反),^(按位异或),<<(左移),>>(右移)
赋值运算符:
空间申请与释放:new ,delete,new[ ],delete[ ]
其他运算符:
成员访问运算符:.
成员指针访问运算符: .,->
域运算符:::
长度运算符: sizeof
条件运算符:?:
预处理运算符:#
class Person {
public:
Person operator+(Person& p)
{
Person temp;
temp.a = this->a + p.a;
temp.b = this->b + p.b;
return temp;
}
int a;
int b;
};
//调用
Person p1;
p1.a = 10;
p1.b = 20;
Person p2;
p2.a = 40;
p2.b = 50;
Person p3 = p1 + p2;
cout << p3.a << p3.b << endl;
Person operator+(Person& p1,Person& p2)
{
Person temp;
temp.a = p1.a + p2.a;
temp.b = p1.b + p2.b;
return temp;
}
//全局函数重载
ostream& operator<<(ostream& cout, Person& p) {
cout << p.a << p.b;
return cout;
}
//实现直接打印对象
Person p;
p.a = 10;
p.b = 12;
cout << p << endl;
成员变量和成员函数分开存储,成员函数和静态变量一样都类的对象上
this指针指向被调用的成员函数所属的对象,存在每个成员函数内部不用被声明
用途:
//可以返回引用 MyClass& 也可以返回对象值 MyClass,如果返回对象值则每次创建一个新对象age不会累加
MyClass& MyClass::PersionAddAge(MyClass &p) {
this->age = this->age + p.age;
//this是一个指针 *this是this对应的值即对象myclass
return *this;
}
MyClass myclass;
myclass.age = 10;
myclass.PersionAddAge(myclass).PersionAddAge(myclass);
cout << myclass.age << endl;
空指针调用成员函数会报错
class Test{
public:
void Test1(){
cout << this-> age << endl;
}
int age;
}
Test* test = null;
test->Test1();//报错因为this为null;
常函数
常对象
常函数怎么声明
class Test{
public:
//声明常函数
//this指针的本质是指针常量 指针的指向不可以修改
//在成员函数后面加const,修饰的是this的指向,让指针指向的值也不能修改
//const Test* const this;
void Test1() const{
age = 10;//报错
}
int age;//加上mutable就可以修改了 mutable int age
}
test->Test1();
在程序里,有些私有的属性也想让类外特殊的一些函数或者类访问,就需要用到友元
关键字friend
友元的三种实现
全部函数做友元
class Building
{
//告诉编译器 全局函数FriendFunc 是building的好友可以访问私有变量
friend void FriendFunc();
public:
Building() {
settingRoom = "客厅";
bedRoom = "卧室";
}
private:
string bedRoom;
public:
string settingRoom;
};
//全局函数
void FriendFunc() {
Building build;
cout << build.settingRoom << endl;
cout << build.bedRoom << endl;
}
//调用
FriendFunc();
类做友元
class Building
{
friend class FriendClass;
friend void FriendFunc();
public:
Building() {
settingRoom = "客厅";
bedRoom = "卧室";
}
private:
string bedRoom;
public:
string settingRoom;
};
class FriendClass {
public:
void Visit(Building* build) {
cout << build->settingRoom << endl;
cout << build->bedRoom << endl;
}
};
Building* build = new Building();
FriendClass firendClass;
firendClass.Visit(build);
成员函数做友元
//让成员函数visit2也可以访问
friend void FriendClass::visit2();
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。