1.c++内存分区
代码:函数体
全局:全局,静态,全局常量,字符串常量
栈:编译器自动分配释放,存放参数值和局部变量
堆:程序员分配和释放,结束时操作系统回收
分区意义,不同区域,生命周期不同
1.1 程序执行前
编译后exe
两个区域
1.1.1 代码区:
cpu执行机器指令
共享:多执行,一份码
只读
1.1.2全局区
全局、静态、字符串
==程序结束==,==操作系统==释放
非全局区:除==静态和字符串==外的所有==局部量==
#include<iostream>
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 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)&"hello world" << endl;
cout << "字符串常量地址为: " << (int)&"hello world1" << endl;
cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;
cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;
const int c_l_a = 10;
const int c_l_b = 10;
cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;
cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl;
system("pause");
return 0;
}
1.2 程序运行
1.2.1 栈区
存储参数值和局部变量
编译器在函数执行完自动分配释放
不要在方法体里返回局部变量地址
int* func()
{
int a = 10;
return &a;
}
int main() {
int *p = func();
cout << *p << endl;//第一次会正确输出,因为编译器会作保留
cout << *p << endl;//d第二次就会有问题
system("pause");
return 0;
}
1.2.2 堆区
由程序员管理开辟和释放
不释放,程序结束时由操作系统回收
利用new关键字进行开辟内存(返回的是地址编号)
指针是局部变量,在栈上。
指针指向的地址在堆区(数据在堆区)
#include<iostream>
using namespace std;
int* func()
{
int* a = new int(10);
return a;
}
int main() {
int* p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl;
system("pause");
return 0;
}
1.2.3 new操作符
语法: int* a = new 数据类型 (值)
创建数组:int* a = new int[10];
释放单个: delete 指针 delete a
释放数组:delete[] arr;
int* func()
{
int* a = new int(10);
return a;
}
int main() {
int* p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl;
system("pause");
delete p;
cout << *p << endl;
return 0;
}
int* arr = new int[10];
delete[] b;
2.引用
给变量起别名
语法: 数据类型 &别名 = 原名
int a = 10;
int &b = a;
2.1 注意事项
1.不能光声明不初始化,一声明就必须初始化
2.引用在初始化后,不可以改变(它永远代表它初始化的那块内存,给引用赋值就是给那块内存赋值,而不是让引用指向新变量)
int a = 10;
int b = 20;
//int &c; //错误,引用必须初始化
int &c = a; //一旦初始化后,就不可以更改
c = b; //这是赋值操作,不是更改引用 不是c变成了b的引用,而是a的值变成了b的值
2.2 引用作形参
指针作参数:传地址
引用作形参:传自身
//1. 值传递
void mySwap01(int a, int b) {
int temp = a;
a = b;
b = temp;
}
//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;
}
mySwap01(a, b);
cout << "a:" << a << " b:" << b << endl;
mySwap02(&a, &b);
cout << "a:" << a << " b:" << b << endl;
mySwap03(a, b);
cout << "a:" << a << " b:" << b << endl;
2.3 引用作返回值
引用是可以作为函数的返回值
如果函数做左值(被赋值对象),那么必须返回引用
注意:不要返回局部变量引用
局部变量地址在栈区,被释放了
int& test01() {
int a = 10; //局部变量
return a;
}
int& test02() {
static int a = 20;
return a;
}
//不能返回局部变量的引用
int& ref = test01();
int& ref2 = test02();
test02() = 1000;
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
2.4 引用的本质
本质:引用的本质在c++内部实现是一个指针常量.(保存地址)
初始化:int& ref = a
; 相当于int* const ref = &a;
赋值:ref = 20
;相当于*ref = 20;
使用的时候只要想操控它就是操控它初始化的那块内存值
上诉转化编译期会自动完成
2.5 常量引用
1.普通引用初始化必须引用合法内存空间,而不能是常量池中的值
//int& ref = 10; 引用本身需要一个合法的内存空间,因此这行错误
int& c = a; //一旦初始化后,就不可以更改
c = 30;
2.常量引用可以直接初始化为常量值
const int& ref =10;
ref = b;//不允许再通过引用赋值了。
编译期会自动优化为 int temp = 10; const int& ref = temp;
3.作用场景
做形参,防止被函数改值
问题:这么做比直接值传递有什么优势
操作时实际上就是操作原对象本身, 这样的传引用可以避免对象传递时产生临时对象. 减少很多不必要的时间和空间的开销
//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {
//v += 10;
cout << v << endl;
}
//函数中利用常量引用防止误操作修改实参
int a = 10;
showValue(a);
3.函数
1.可有默认参数
一个有默认值,则从它往后的每一个参数都需要有
int func(int a, int b = 10, int c = 10) {
return a + b + c;
}
2.可有占位参数,必须填补
void func(int a, int) {
cout << "this is func" << endl;
}
func(10,10); //占位参数必须填补
3.3函数重载
3.3.1 重载条件
- 同一个作用域下
- 函数名称相同
- 函数参数类型不同 或者 个数不同 或者 顺序不同
注意: 函数的返回值类型不同不可以作为函数重载的条件
#include<iostream>
using namespace std;
void func(double a, int b)
{
cout << "func (double a ,int b)的调用!" << endl;
}
void func(double a, int b)
{
cout << "func (double a ,int b)的调用!" << endl;
}
int main() {
func(3.14, 10);
system("pause");
return 0;
}
3.3.2 注意事项
1.引用作为参数类型
常量引用直接传值,非常量引用传参数名
void func(int &a)
{
cout << "func (int &a) 调用 " << endl;
}
void func(const int &a)
{
cout << "func (const int &a) 调用 " << endl;
}
int a = 10;
func(a); //调用无const
func(10);//调用有const
2.默认参数导致歧义问题
void func2(int a, int b = 10)
{
cout << "func2(int a, int b = 10) 调用" << endl;
}
void func2(int a)
{
cout << "func2(int a) 调用" << endl;
}
func2(10);
4.类
三大特性:封装,继承。多态
C++认为万事万物都皆为对象,对象上有其属性和行为
4.1 封装 encapsulation
1.意义:把属性和行为作为一个整体
const double PI = 3.14;
class Circle {
public:
int m_r;
double getZC() {
return 2 * PI * m_r;
}
};
int main() {
Circle c1;
c1.m_r = 2;
cout << c1.getZC() << endl;
system("pause");
return 0;
}
2. 访问控制:
- public 公共权限 类内可以访问 类外可以访问
- protected 保护权限 类内可以访问 类外不可以访问 儿子可访问
- private 私有权限 儿子不能访问
class Circle {
public:
int m_r;
double getZC() {
return 2 * PI * m_r;
}
void func() {
m_type = "circle";
password = 123;
}
protected:
string m_type;
private:
int password;
};
int main() {
Circle c1 ;
cout << c1.getZC() << endl;
system("pause");
return 0;
}
3.和struct区别
在C++中 struct和class唯一的区别就在于 默认的访问权限不同
class C1 {
int m_1;
};
struct C2 {
int m_2;
};
C1 c1;
c1.m_1 = 1;//会报错
C2 c2 ;
c2.m_2 = 1;
4.成员属性私有化
原因:
1.可以自己控制读写权限
2.可以检测写操作有效性
class Person {
public:
//姓名设置可读可写
void setName(string name) {
m_Name = name;
}
string getName()
{
return m_Name;
}
//获取年龄
int getAge() {
return m_Age;
}
//设置年龄
void setAge(int age) {
if (age < 0 || age > 150) {
cout << "你个老妖精!" << endl;
return;
}
m_Age = age;
}
//情人设置为只写
void setLover(string lover) {
m_Lover = lover;
}
private:
string m_Name; //可读可写 姓名
int m_Age; //只读 年龄
string m_Lover; //只写 情人
};
int main() {
Person p;
//姓名设置
p.setName("张三");
cout << "姓名: " << p.getName() << endl;
//年龄设置
p.setAge(50);
cout << "年龄: " << p.getAge() << endl;
//情人设置
p.setLover("苍井");
//cout << "情人: " << p.m_Lover << endl; //只写属性,不可以读取
system("pause");
return 0;
}
3.形参尽量传引用,取值尽量用方法
class Cube {
private:
double a;
public:
double getA() {
return a;
}
void setA(double x) {
a = x;
}
bool myEquals(Cube &x1) {
return x1.getA() == a;
}
double getArea() {
return a * a;
}
double getVolume() {
return a * a * a;
}
};
bool myEqual1(Cube& x1, Cube& x2) {
return x1.getA() == x2.getA();
}