C++程序进阶学习
目录
引言
看过我博客的朋友可能都了解这篇文章内容了,这篇博客是由我单独每天撰写的C++内容合集,重新整理完善了内容;也算是自己重新学习,也和大家一起学习,如果对大家的学习有帮助,那自然更好,如果大家在学习的过程中发现文章内容有问题或者不懂的,希望大家能在评论区积极讨论,我看到了也会回复!!!
C++内存分区
一、内存分区模型
在执行C++程序时,我们可将内存划分为四个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理(我们一般的代码都在里面)
- 全局区:存放全局变量和静态变量以及常量
- 栈区:存放函数的参数值,局部变量等,这个区域则由编译器自动分配释放
- 堆区:这一区域则是程序员自己分配和释放,程序结束时也会由系统回收
为什么要区分这些区域呢?
将数据放在不同区域,数据所占空间的时间不同,这也使我们能更灵活的使用数据。
二、 程序运行前
在程序编译后,未执行该程序前分为两个部分
1.代码区:
存放CPU执行的机器指令
我们需要注意代码区是只读不写的
2.全局区:
三、程序执行后
在程序执行后可分为两部分
栈区:这里我们需要注意的是,在编写程序时,不能返回局部变量的地址
示例:
#include<iostream>
using namespace std;
//栈区的数据由编译器管理开辟和释放
int* func()//形参数据也放在栈区
{
int c_a = 10;
return &c_a;
}
int main()
{
int * p = func();
cout << *p << endl;//第一次可以打印正确的数字,这个时候他的值以及被编译器释放了,只是因为编译器做了保留
cout << *p << endl;//第二次这个数据就不再保留
system("pause");
return 0;
}
堆区:在C++中主要利用new在堆区开辟内存
示例:
#include<iostream>
using namespace std;
int* func()
{
int * p = new int(1);
return p;
}
int main()
{
int*p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl;//只要我们没有人为的去释放内存,是可以一直输出的
system("pause");
return 0;
}
C++引用
引用的语法
数据类型 & 别名 = 原名
作用
1、函数传参时,可以利用引用技术让形参修饰实参
2、引用是可以作为函数的返回值存在的(同样不可以返回局部变量引用)
本质
引用的本质在C++内部实现一个指针常量
优点
可以简化指针修改实参
示例1:
#include<iostream>
using namespace std;
int main()
{
int a = 1;
int &b = a;//创建引用
cout << "a=" << a << endl;//输出均是1
cout << "b=" << b << endl;
b = 2;
cout << "a=" << a << endl;//输出均是2
cout << "b=" << b << endl;
system("pause");
return 0;
}
注意:
1.引用必须要初始化
2.一旦初始化了,就不可以更改了!!!!
我们也同样可以来看一下引用其本质,下面一个示例就能很好的说明:
示例2:
#include<iostream>
using namespace std;
//发现是引用,转换为 int* const b = &a;
void func(int &b)
{
b = 100;//ref是引用,转换为*b = 100
}
int main()
{
int a = 10;
//自动转换为 int* const b = &a,这就相当于一个指针常量,指针常量我们都知道是指针指向不可改,这也充分说明了我们之前为什么说引用不可更改
int &b = a;
ref = 20;//当编译器内部发现 ref 是引用,编译器自动将其转换为:*b=20;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
func(a);
system("pause");
return 0;
}
当我们在编写程序时就无需考虑其是怎样去转换的,讲解引用本质,只是为了帮助我们理解。我们只需掌握引用的语法结构、作用以及注意事项!!!
C++封装
C++认为万事万物都皆为对象,对象上有其属性和行为
类在设计时,可以把属性和行为加以控制,可以设置三种权限:
- public 公共权限 成员 类内可以访问 类外可以访问
- protected 保护权限 成员 类内可以访问 类外不可以访问
- private 私有权限 成员 类内可以访问 类外不可以访问
这里使用class去创建类,前面我们学习了struct创建体,二者并没有什么差别,struct和class唯一的区别就是默认的访问权限不同:struct默认权限为公共,class默认权限为私有
举一个简单的例子,求圆的周长:
首先我们进行简单分析:我们知道圆的周长公式为2ΠR,Π是个定数,一般使用3.14,简单定义就行;R就是需要去设置的,这里我们就设计一个类,一个类里面包含属性和行为,圆的属性有圆心、半径或者直径,这里我们只需要设置一个半径就可以了,行为:就是我们要求的内容,设定为一个函数。下面就是代码:
#include<iostream>
using namespace std;
const double PI = 3.14;
//class 代表设计一个类,类后面紧跟着的就是类的名称
class Circle
{
public: //公共权限
//属性
//半径
int m_r;
//行为
//获取圆的周长
double calculateZC()
{
return 2 * PI * m_r;
}
};
int main()
{
//实例化 创建一个对象
Circle c;
//给圆对象 的属性进行赋值
c.m_r = 10;
cout << "圆的周长:" << c.calculateZC() << endl;
system("pause");
return 0;
}
上面代码我们使用了最简单的类的创建,就是将属性都统一放置在公共权限中,但在我们通常编写程序时将这些放在私有权限,将设置数据和调取数据函数放在公共权限,这样有以下两个优点:
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
我们同样以求圆周为例:
#include<iostream>
using namespace std;
//圆周率
const double PI = 3.14;
//class 代表设计一个类,类后面紧跟着的就是类的名称
class Circle
{
public://公共权限
// 设置半径
void setR(int r)
{
m_R = r;
}
// 读取半径
int getR()
{
return m_R;
}
//行为
//获取圆的周长
double getZC()
{
return 2 * PI * m_R;
}
//私密权限
private:
//属性
//半径
int m_R;
};
int main()
{
//实例化 创建具体的圆(对象)
Circle c;
//给圆对象 的属性进行赋值
c.setR(10);
cout << "圆的周长:" << c.getZC() << endl;
system("pause");
return 0;
}
这样我们就能很好的控制数据的权限,在编写程序时需要分辨这个数据,在主函数中是否需要修改,是否需要读取,只有这样才能尽可能的减少编写错误。
C++对象特性
在C++中,类的构造函数和析构函数是处理对象初始化和清理的关键。下面是构造函数和析构函数的详细解释:
一、构造函数
构造函数用于对象的初始化。构造函数的特点和使用如下:
- 函数名与类名相同:构造函数的名称必须和类的名称完全相同,这样编译器才能识别它是构造函数。
- 没有返回值:构造函数没有返回值类型,因此不需要写
void
或其他返回类型。 - 可以有参数,可以重载:构造函数可以有参数,可以根据不同参数进行重载,这使得可以用不同方式初始化对象。
- 自动调用:在创建对象的时候,构造函数会自动调用,而且每个对象的构造函数只会调用一次。
二、析构函数
析构函数用于对象销毁前的清理操作。析构函数的特点和使用如下:
- 函数名与类名相同,前加
~
:析构函数的名称与类名相同,但前面要加上波浪号~
。 - 没有返回值:析构函数也没有返回值类型,因此不需要写
void
或其他返回类型。 - 不能有参数,不能重载:析构函数不能有参数,也不能被重载。
- 自动调用:对象在销毁前会自动调用析构函数,每个对象的析构函数也只会调用一次。
下列写了三类函数的语法,以及三种调用方式:
#include<iostream>
using namespace std;
class Person
{
public:
//构造函数
Person()
{
cout << "Person的无参构造函数调用" << endl;
}
Person(int a)
{
age = a;
cout << "Person的有参构造函数调用" << endl;
}
//拷贝构造函数
Person(const Person &p)
{
//将传入的人的身上的所有属性,拷贝到我身上
cout << "Person的拷贝构造函数调用" << endl;
age = p.age;
}
~Person()
{
cout << "Person的析构函数调用 " << endl;
}
int age;
};
void test()
{
//1、括号法
Person p1;//默认构造函数调用
Person p2(10);//有参构造函数
Person p3(p2);//拷贝构造函数
//2、显示法
Person p1;
Person p2 = Person(10);//有参构造
Person p3 = Person(p2);//拷贝构造
//3、隐式转换法
Person p4 = 10;//相当于 写了 Person p4 = Person(10)
Person p5 = p4;
}
int main()
{
test();
system("pause");
return 0;
}
在C++中,构造函数的调用和生成有一些特定的规则。了解这些规则有助于正确管理类的构造和销毁。以下是详细的说明:
1. 编译器默认添加的函数
当你定义一个类时,C++编译器会自动为类添加至少三个默认函数:
- 默认构造函数:如果你没有定义任何构造函数,编译器会提供一个无参的默认构造函数。
- 析构函数:编译器会提供一个默认的析构函数,用于对象销毁时的清理工作。
- 拷贝构造函数:编译器会提供一个默认的拷贝构造函数,该函数执行成员变量的浅拷贝(值拷贝)。
示例代码:
class MyClass {
// 如果没有定义任何构造函数、析构函数或拷贝构造函数
// 编译器会自动生成以下函数:
public:
MyClass(); // 默认构造函数
~MyClass(); // 默认析构函数
MyClass(const MyClass& other); // 默认拷贝构造函数
};
2. 有参构造函数的影响
- 覆盖默认构造函数:如果你定义了一个有参构造函数,编译器就不再提供默认的无参构造函数。
- 依然提供拷贝构造函数:即使定义了有参构造函数,编译器仍然会提供默认的拷贝构造函数(如果没有显式定义)。
示例代码:
class MyClass {
public:
MyClass(int value); // 有参构造函数
// 编译器不会再提供默认构造函数
// 编译器依然会提供默认的拷贝构造函数和析构函数
};
3. 拷贝构造函数的影响
- 覆盖默认拷贝构造函数:如果你定义了拷贝构造函数,编译器就不再提供默认的拷贝构造函数。
- 不影响其他普通构造函数:即使定义了拷贝构造函数,编译器仍然会提供默认构造函数和析构函数(如果没有显式定义)。
示例代码:
class MyClass {
public:
MyClass(const MyClass& other); // 拷贝构造函数
// 编译器不会再提供默认的拷贝构造函数
// 编译器依然会提供默认构造函数和析构函数(如果没有显式定义)
};
4. 手动定义所有特殊成员函数
在实际开发中,通常会根据需要手动定义所有的特殊成员函数,以确保对象的正确构造和销毁。
示例代码:
class MyClass {
public:
MyClass() {
// 自定义默认构造函数
}
MyClass(int value) {
// 自定义有参构造函数
}
MyClass(const MyClass& other) {
// 自定义拷贝构造函数
}
~MyClass() {
// 自定义析构函数
}
};
通过理解这些规则,可以更好地控制对象的生命周期,避免潜在的内存泄漏和其他问题。
后续我将逐步整合,收藏不迷路!!!大家感兴趣,也可以先看看我之前的博客!!!
C++对象模型和this指针
C++友元
C++运算符重载
C++继承
C++多态
C++文件
原文地址:https://blog.csdn.net/JH_joker/article/details/140362943
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!