本阶段主要针对c++面向对象编程技术做详细讲解,探讨c++中的核心和精髓
c++程序在执行前,将内存大方向划分为4个区域
内存四区意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程
在程序编译后,生成EXE可执行程序,未执行该程序之前分为两个区域
代码区:
存放cpu执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量、静态变量和常量存放在此
全局区还包含了常量区,字符串区和其他变量也存放在此
该区域的数据在程序结束后由操作系统释放
全局变量:不在main函数及其他函数中的变量
#include<iostream>
#include<string>
using namespace std;
//全局变量
int g_a = 10;
int g_b = 10;
const int c_g_a = 10;
const int c_g_b = 10;
int main()
{
int a = 10;
int b = 10;
cout << "局部变量a的地址为:" << (int)&a << endl;
cout << "局部变量b的地址为:" << (int)&b << endl;
cout << "全局变量g_a的地址为:" << (int)&g_a << endl;
cout << "全局变量g_b的地址为:" << (int)&g_b << endl;
//静态变量,在普通变量前加static,属于静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量s_a的地址为:" << (int)&s_a << endl;
cout << "静态变量s_b的地址为:" << (int)&s_b << endl;
cout << "字符串常量的地址为:" << (int)&"helloworld" << endl;
cout << "常量之中const修饰的全局常量c_g_a的地址为:" << (int)&c_g_a << endl;
cout << "常量之中const修饰的全局常量c_g_b的地址为:" << (int)&c_g_b << endl;
const int c_l_a = 10;
const int c_l_b = 10;
cout << "常量之中const修饰的局部变量c_l_a的地址为:" << (int)&c_l_a << endl;
cout << "常量之中const修饰的局部变量c_l_b的地址为:" << (int)&c_l_b << endl;
system("pause");
return 0;
}
栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
#include<iostream>
using namespace std;
int* func()
{
int a = 10; //这个局部变量存放在栈区,栈区的数据在函数执行完之后自动释放
return &a;
}
int main()
{
int* p = func();
cout << *p << endl; //第一次可以打印正确的数字,是因为编译器做了保留
cout << *p << endl; //第二次这个数据就不再保留
system("pause");
return 0;
}
但是在x64位的情况下,第二次的数据还会保留
堆区
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在c++中主要利用new在堆区开辟内存
#include<iostream>
using namespace std;
int* func()
{
int* p = new int(10); //new int(10)这里返回的是地址编号
return p;
}
int main()
{
int* p = func();
cout << *p << endl;
cout << *p << endl; //堆区的数据由程序员分配释放
system("pause"); //若程序员不释放,程序结束时由操作系统回收
return 0;
}
c++利用new操作符在堆区开辟内存
堆区开辟的数据空间,由程序员手动开辟,手动释放,释放利用操作符delete
语法:new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针
#include<iostream>
using namespace std;
//1.new的基本语法
int* func()
{
int* p = new int(10); //在堆区创建一个整型数据,new返回的是该数据类型的指针
return p; //new一个什么类型,返回一个什么类型的指针
}
void test01()
{
int* p = func();
cout << *p << endl;
cout << *p << endl;
delete p; //程序员利用delete对堆区数据进行释放
cout << *p << endl; //释放之后再对其进行输出则会报错
}
int main()
{
test01();
system("pause");
return 0;
}
#include<iostream>
using namespace std;
//2.利用new创建堆区数组
void test02()
{
int* arr = new int[10]; //堆区创建数组的语法
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
delete[]arr; //堆区数组的释放语法
for (int i = 0; i < 10; i++) //释放之后再输出就会报错
{
cout << arr[i] << endl;
}
}
int main()
{
test02();
system("pause");
return 0;
}
作用:给变量起别名
语法:数据类型 &别名=原名
引用类型要与原名类型一致
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int& b = a;
cout << "改变之前a=" << a << endl;
cout << "改变之前b=" << b << endl;
b = 1000;
cout << "改变之后a=" << a << endl;
cout << "改变之后b=" << b << endl;
}
/*输出结果为
改变之前a=10
改变之前b=10
改变之后a=1000
改变之后b=1000
*/
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int& b = a;
//int& c; //会报错,引用必须初始化
int c = 20;
b = c; //这是赋值操作,不是更改引用
//&b = c; //编译器报错,这是更改引用,不能这样做——不可以更改引用
cout << a << endl;
cout << b << endl;
cout << c << endl;
system("pause");
return 0;
}
作用:函数做参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
#include<iostream>
using namespace std;
//1.值传递
void myswap01(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "值传递中的a=" << a << endl;
cout << "值传递中的b=" << b << endl;
}
//2.地址传递
void myswap02(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//3.引用传递
void myswap03(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
myswap01(a, b);
cout << "值传递交换后的a=" << a << endl;
cout << "值传递交换后的b=" << b << endl;
myswap02(&a, &b);
cout << "地址传递交换后的a=" << a << endl;
cout << "地址传递交换后的b=" << b << endl;
myswap03(a, b);
cout << "引用传递交换后的a=" << a << endl;
cout << "引用传递交换后的b=" << b << endl;
system("pause");
return 0;
}
/*
值传递中的a=20
值传递中的b=10
值传递交换后的a=10
值传递交换后的b=20
地址传递交换后的a=20
地址传递交换后的b=10
引用传递交换后的a=10
引用传递交换后的b=20
*/
作用:引用是可以作为函数的返回值而存在的
注意:不要返回局部变量引用
用法:函数调用作为左值
#include<iostream>
using namespace std;
//1.不要返回局部变量的引用
int& test01() //如果不加那个&符的话,会报错
{
int a = 10;
return a;
}
int main()
{
int& b = test01();
cout << b << endl; //第一次可以成功输出,是因为编译器做了保留
cout << b << endl; //第二次结果错误,是因为局部变量在栈区,函数运行结束释放
system("pause");
return 0;
}
#include<iostream>
using namespace std;
//2.函数的调用可以作为左值
int& test02()
{
static int a = 10; //静态变量,存放在全局区
return a; //全局区上的数据在程序结束后系统释放
}
int main()
{
int& b = test02();
cout << b << endl;
cout << b << endl;
test02() = 1000; //如果函数的返回值是引用,这个函数调用可以作为左值
cout << b << endl;
system("pause");
return 0;
}
本质:引用的本质在c++内部实现是一个指针常量
#include<iostream>
using namespace std;
void func(int& ref)
{
ref = 100; //ref是引用,转换为*ref=100
}
int main()
{
int a = 10;
//自动转化为int * const ref=&a;指针常量是指针指向不可改,也说明为什么引用不可更改
int& ref = a;
ref = 20; //内部发现ref是引用,自动帮我们转换为*ref=20;
cout << "a:" << a << endl;
cout << "ref:" << ref << endl;
func(a);
system("pause");
return 0;
}
使用场景:修饰形参,防止误操作
#include<iostream>
using namespace std;
int main()
{
int a = 10;
//int& ref = 10; //这句是错误的,引用必须引用一块合法的内存空间(栈区或堆区数据)
const int& ref = 10;
//编译器内部 int temp=10;const int &ref=temp;
//ref = 20; //这句也是错误的,加入const之后ref变为只读,不可修改
system("pause");
return 0;
}
#include<iostream>
using namespace std;
void showValue(int& value)
{
value = 1000;
cout << "value=" << value << endl;
}
void showValue1(const int& value)
{
//value = 1000; 此句报错,此函数中value的值不可以被修改
cout << "value=" << value << endl;
}
int main()
{
int a = 10;
showValue(a);
cout << "函数调用之后的a=" << a << endl;
system("pause");
return 0;
}
在c++中,函数的形参列表中的形参可以有默认值
语法:返回值类型 函数名 (参数=默认值) {}
#include<iostream>
using namespace std;
int func(int a, int b = 20, int c = 30)
{
return a + b + c;
}
int main()
{
cout << func(10) << endl;
cout << func(10, 30) << endl;
system("pause");
return 0;
}
注意:
#include<iostream>
using namespace std;
int func(int a = 10, int b = 20, int c = 30);
int func(int a = 10, int b = 20, int c = 30) //会报错,因为有二义性,编译器不知道是跟随声明还是实现
{
return a + b + c;
}
int main()
{
cout << func(10) << endl;
cout << func(10, 30) << endl;
system("pause");
return 0;
}
#include<iostream>
using namespace std;
int func(int a = 10, int b = 20, int c = 30);
int func(int a, int b, int c) //会有二义性,编译器不知道是跟随声明还是实现
{
return a + b + c;
}
int main()
{
cout << func(10) << endl;
cout << func(20, 60, 50) << endl;
system("pause");
return 0;
}
c++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法:返回值类型 函数名 (数据类型){}
在现阶段函数的占位参数意义不大,但后面的课程会用到该技术
#include<iostream>
using namespace std;
//占位参数
//现阶段占位时,只能用到第一个10,用不到第二个占位的那个整型数据
void func(int a, int)
{
cout << "this is func" << endl;
}
void func1(int a, int = 30)
{
cout << "this is func1" << endl;
}
int main()
{
func(10, 5);
func1(10, 20);
system("pause");
return 0;
}
作用:函数名可以相同,提高复用性
函数重载满足条件:
函数的返回值不可以作为函数重载的条件
#include<iostream>
using namespace std;
//函数重载
//1.在同一个作用域下,即都不在main函数都为全局作用域
void function()
{
cout << "function()的调用" << endl;
}
void function(int a)
{
cout << "function(int a)的调用" << endl;
}
void function(double a)
{
cout << "function(double a)的调用" << endl;
}
void function(int a, double b)
{
cout << "function(int a,double b)的调用" << endl;
}
void function(double a, int b)
{
cout << "function(double a,int b)的调用" << endl;
}
int main()
{
function();
function(10);
function(3.1415);
function(10, 3.1415);
function(3.1415, 20);
system("pause");
return 0;
}
当函数重载碰到默认参数,会出现二义性,程序会报错,尽量避免
#include<iostream>
using namespace std;
//1.引用作为重载条件
void fun(int& a)
{
cout << "fun(int &a)调用" << endl;
}
void fun(const int& a)
{
cout << "fun(const int& a)调用" << endl;
}
int main()
{
int a = 10;
fun(a); //调用第一个函数
fun(10); //调用第二个函数
system("pause");
return 0;
}
#include<iostream>
using namespace std;
//2.函数重载有默认参数
void fun(int a, int b = 10)
{
cout << "fun(int a,int b=10)调用" << endl;
}
void fun(int a)
{
cout << "fun(int a)调用" << endl;
}
int main()
{
int a = 10;
//fun(a); //会报错,因为两个函数都可以
fun(10, a);
system("pause");
return 0;
}
c++中面向对象的三大特性为:封装、继承、多态
c++认为万事万物都皆为对象,对象有其属性和行为
具有相同性质的对象,可以抽象为类。
eg:
人可以作为对象,属性有姓名、年龄、身高、体重…,行为有走、跑、跳、吃饭、唱歌…
封装是c++面向对象三大特性之一
封装的意义:
封装意义一:
在设计类的时候,属性和行为写在一起,表现事物
语法:class 类名 {访问权限 : 属性/行为};
#include<iostream>
using namespace std;
const double PI = 3.14;
//设计一个圆类,求圆的周长
class Circle
{
//访问权限
public:
//属性,一般使用变量
int m_r;
//行为,一般使用函数
double calculateZC()
{
return 2 * PI * m_r;
}
};
int main()
{
//通过圆类 创建具体的圆(对象)
//实例化——(通过一个类,创建一个对象的过程)
Circle c1;
c1.m_r = 10; //给属性赋值
cout << "圆的周长为:" << c1.calculateZC() << endl;
system("pause");
return 0;
}
示例:设计一个学生类,属性有姓名和学号,可以给学生的姓名和学号赋值,可以显示学生的姓名和学号
#include<iostream>
#include<string>
using namespace std;
//设计学生类
class Student
{
//访问权限
public: //公共权限
//属性,一般使用变量
string m_Name; //姓名
int m_Id; //学号
//行为,一般使用函数
void showStudent()
{
cout << "姓名为:" << m_Name << '\t';
cout << "学号为:" << m_Id << endl;
}
};
int main()
{
//通过学生类 创建具体的学生(对象)
Student s1;
s1.m_Name = "张三";
s1.m_Id = 1;
//显示学生信息
s1.showStudent();
Student s2;
s2.m_Name = "李四";
s2.m_Id = 2;
//显示学生信息
s2.showStudent();
system("pause");
return 0;
}
#include<iostream>
#include<string>
using namespace std;
//设计学生类
class Student
{
//访问权限
public: //公共权限
//类中的属性和行为,一般统称为成员
// 属性 也称为成员属性 成员变量
// 行为 也称为成员函数 成员方法
//属性,一般使用变量
string m_Name; //姓名
int m_Id; //学号
//行为,一般使用函数
void showStudent()
{
cout << "姓名为:" << m_Name << '\t';
cout << "学号为:" << m_Id << endl;
}
//2.利用行为给属性赋值
void setName(string name)
{
m_Name = name;
}
void setId(int id)
{
m_Id = id;
}
};
int main()
{
//通过学生类 创建具体的学生(对象)
Student s1;
s1.setName("张三");
s1.setId(1);
//显示学生信息
s1.showStudent();
system("pause");
return 0;
}
封装的意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:
#include<iostream>
using namespace std;
class Person
{
public:
string m_Name;
protected: //成员类内可以访问,类外不可以访问
string m_Car; //儿子可以访问父亲中的保护内容
private: //成员类内可以访问,类外不可以访问
int m_password; //儿子不可以访问父亲中的私有内容
public:
void func()
{
m_Name = "张三";
m_Car = "拖拉机";
m_password = 123456;
}
};
int main()
{
Person p1;
p1.m_Name = "李四";
//p1.m_Car = "奔驰"; //保护权限内容在类外访问不到
//p1.m_password = 123; //私有权限内容在类外访问不到
p1.func();
system("pause");
return 0;
}
在c++中,struct和class唯一的区别就是在于默认访问权限不同
区别:
struct默认权限为公共——public
class默认权限为私有——private
#include<iostream>
using namespace std;
class C1
{
int m_a;
};
struct C2
{
int m_a;
};
int main()
{
C1 c1;
// c1.m_a = 100; //会报错
C2 c2;
c2.m_a = 1000; //不会报错
system("pause");
return 0;
}
优点1:将所有成员设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
//姓名的权限是可读可写
void setName(string name) //设置姓名
{
m_Name = name;
}
string getName() //获取姓名
{
return m_Name;
}
//年龄的权限也是可读可写,但是要设置0-150岁的范围
void setAge(int age)
{
if (age > 0 && age <= 150)
{
m_Age = age;
return;
}
m_Age = 0;
}
int getAge()
{
return m_Age;
}
//情人的权限是只写
void setLover(string lover)
{
m_Lover = lover;
}
private:
string m_Name;
int m_Age;
string m_Lover;
};
int main()
{
Person p1;
p1.setName("张三");
p1.setAge(1000);
p1.setLover("小王");
cout << "姓名为:" << p1.getName() << endl;
cout << "年龄为:" << p1.getAge() << endl;
system("pause");
return 0;
}
案例1:
设计一个立方体类
求出立方体的面积和体积
分别用全局函数和成员函数判断两个立方体是否相等
#include<iostream>
#include<string>
using namespace std;
/*设计步骤
1.创建立方体类
2.设计属性
3.设计行为——获取立方体面积和体积
4.分别利用全局函数和成员函数判断两个立方体是否相等
*/
class Cube
{
public:
//设置长
void setL(int l)
{
m_L = l;
}
//获取长
int getL()
{
return m_L;
}
//设置宽
void setW(int w)
{
m_W = w;
}
//获取宽
int getW()
{
return m_W;
}
//设置高
void setH(int h)
{
m_H = h;
}
//
文章浏览阅读6.2k次,点赞6次,收藏26次。我是一个深度学习代码小白,请你用中文写上注释,能让我能轻松理解下面这段代码。注意包含所有函数、调用和参数的注释。以同样的python代码块样式返回你写的代码给我。代码看累了,就看《动手学深度学习》文档:基于PyTorch框架,从底层函数实现基础功能,再到框架的高级功能。努力上路的小白一枚,麻烦路过的大佬指导一二,同时希望能和大家交流学习~争取更新学习这个文档的专栏,记录学习过程。量身定做了一套话术hhh,亲身测试还不错。这个感觉更浅一点儿,之后复习看吧。20天吃掉那只Pytorch。_深度学习程序怎么读
文章浏览阅读2.7w次,点赞126次,收藏1.2k次。耗废1024根秀发,Java学习路线图来了,整合了自己所学的所有技术整理出来的2022最新版Java学习路线图,适合于初、中级别的Java程序员。_java学习路线
文章浏览阅读4.4k次。1.7-savingPNG介绍代码详情函数详解savePNGFile()源码savePNGFile()源码提示savePNGFile()推荐用法处理结果代码链接介绍PCL提供了将点云的值保存到PNG图像文件的可能性。这只能用有有序的云来完成,因为结果图像的行和列将与云中的行和列完全对应。例如,如果您从类似Kinect或Xtion的传感器中获取了点云,则可以使用它来检索与该云匹配的640x480 RGB图像。代码详情#include <pcl / io / pcd_io.h>#incl_pcl::io:savepng
文章浏览阅读936次。吸引妹子的关键点不在于喝什么咖啡,主要在于竖立哪种男性人设。能把人设在几分钟内快速固定下来,也就不愁吸引对口的妹子了。我有几个备选方案,仅供参考。1. 运动型男生左手单手俯卧撑,右手在键盘上敲代码。你雄壮的腰腹肌肉群活灵活现,简直就是移动的春药。2.幽默男生花 20 块找一个托(最好是老同学 or 同事)坐你对面。每当你侃侃而谈,他便满面涨红、放声大笑、不能自已。他笑的越弱_咖啡厅写代码
文章浏览阅读1.2w次,点赞5次,收藏5次。今天 (应该是昨天了,昨晚太晚了没发出去)下午参加了腾讯WXG的面委会面试。前面在牛客上搜索了面委会相关的面经普遍反映面委会较难,因为都是微信的核心大佬,问的问题也会比较深。昨晚还蛮紧张的,晚上都没睡好。面试使用的是腾讯会议,时间到了面试官准时进入会议。照例是简单的自我介绍,然后是几个常见的基础问题:例如数据库索引,什么时候索引会失效、设计模式等。这部分比较普通,问的也不是很多,不再赘述。现在回想下,大部分还是简历上写的技能点。接下来面试官让打开项目的代码,对着代码讲解思路。我笔记本上没有这部分代码,所_腾讯面委会面试是什么
文章浏览阅读382次,点赞3次,收藏4次。AI绘画自动生成器是一种利用人工智能技术,特别是深度学习算法,来自动创建视觉艺术作品的软件工具。这些工具通常基于神经网络模型,如生成对抗网络(GANs),通过学习大量的图像数据来生成新的图像。AI绘画自动生成器作为艺术与科技结合的产物,正在开启艺术创作的新篇章。它们不仅为艺术家和设计师提供了新的工具,也为普通用户提供了探索艺术的机会。随着技术的不断进步,我们可以预见,AI绘画自动生成器将在未来的创意产业中发挥越来越重要的作用。
文章浏览阅读1.7k次。理解为ListView 的三种形式吧ListView 默认构造但是这种方式创建的列表存在一个问题:对于那些长列表或者需要较昂贵渲染开销的子组件,即使还没有出现在屏幕中但仍然会被ListView所创建,这将是一项较大的开销,使用不当可能引起性能问题甚至卡顿直接返回的是每一行的Widget,相当于ios的row。行高按Widget(cell)高设置ListView.build 就和io..._flutter listview.separated和listview.builder
文章浏览阅读1.4k次,点赞4次,收藏14次。废话不多说直接上干货1.js运行机制JavaScript单线程,任务需要排队执行同步任务进入主线程排队,异步任务进入事件队列排队等待被推入主线程执行定时器的延迟时间为0并不是立刻执行,只是代表相比于其他定时器更早的被执行以宏任务和微任务进一步理解js执行机制整段代码作为宏任务开始执行,执行过程中宏任务和微任务进入相应的队列中整段代码执行结束,看微任务队列中是否有任务等待执行,如果有则执行所有的微任务,直到微任务队列中的任务执行完毕,如果没有则继续执行新的宏任务执行新的宏任务,凡是在..._前端面试
文章浏览阅读1k次。(3)若没有查到,则将请求发给根域DNS服务器,并依序从根域查找顶级域,由顶级查找二级域,二级域查找三级,直至找到要解析的地址或名字,即向客户机所在网络的DNS服务器发出应答信息,DNS服务器收到应答后现在缓存中存储,然后,将解析结果发给客户机。(3)若没有查到,则将请求发给根域DNS服务器,并依序从根域查找顶级域,由顶级查找二级域,二级域查找三级,直至找到要解析的地址或名字,即向客户机所在网络的DNS服务器发出应答信息,DNS服务器收到应答后现在缓存中存储,然后,将解析结果发给客户机。_linux
文章浏览阅读7.9k次,点赞26次,收藏66次。HTML DOM——文档元素的操作1、通过id获取文档元素任务描述相关知识什么是DOM文档元素节点树通过id获取文档元素代码文件2、通过类名获取文档元素任务描述相关知识通过类名获取文档元素代码文件3、通过标签名获取文档元素任务描述相关知识通过标签名获取文档元素获取标签内部的子元素代码文件4、html5中获取元素的方法一任务描述相关知识css选择器querySelector的用法代码文件5、html5中获取元素的方法二任务描述相关知识querySelectorAll的用法代码文件6、节点树上的操作任务描述相关_javascript学习手册十三:html dom——文档元素的操作(一)
文章浏览阅读132次。《LeetCode学习》172. 阶乘后的零(java篇)_java 给定一个整数n,返回n!结果尾数中零的数量
文章浏览阅读426次。请注意,本文将要给大家分享的并不是开启公众号的安全操作风险提醒,而是当公众号粉丝给公众号发消息的时候,公众号的管理员和运营者如何能在手机上立即收到消息通知,以及在手机上回复粉丝消息。第一步:授权1、在微信中点击右上角+,然后选择“添加朋友”,然后选择“公众号”,然后输入“微小助”并关注该公众号。2、进入微小助公众号,然后点击底部菜单【新增授权】,如下图所示:3、然后会打开一个温馨提示页面。请一定要..._php微信公众号服务提示