C++之核心编程 | 分区 引用 函数重载 类和对象

1 内存分区模型

c++程序执行时,将内存大小方向划分为4个区域:

  • 代码区:存放函数体的二进制代码,由操作系统进行管理(所有代码)
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配和释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回放

内存四区意义:
不同区域存放的数量,赋予不同的生命周期,给我们更大的灵活编程


1.1 程序运行前

程序编译后,生成了exe可执行程序,未执行程序前后分为两个区域

代码区:
存放cpu执行的机器指令
代码区是共享的
代码区是只读的

全局区:
全局变量和静态变量存放在此
全局区包含了常量区、字符常量区和其他常量区

(该区域的数据在程序结束后由此操作系统释放)

1.2 程序运行后

栈区:
由编译器自动分配释放,存放函数的参数值,局部变量等


  • 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

堆区:
由程序员分配和释放,若程序员不释放,程序结束时由操作系统回放
在C++中主要利用new在堆区开辟内存

1.3 new 操作符

C++中利用new操作符在堆区开服数据
堆区开辟数据,由程序员手动开辟,手动释放,释放利用操作符delete

语法: new 数据类型

利用new创建的数据,会返回该数据对应的类型的指针

2 引用

等价于指针作用,比指针简单

2.1 引用的基本使用

作用:给变量起别名
语法:数据类型 &别名 = 原名

int main()
{
	int a = 10;
	int& b = a;
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;

	b = 50;
	
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;

	system("pause");
	return 0;
}

2.2 引用注意事项

  • 引用必须初始化
  • 引用在初始化后,不可改变

2.3 引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参

2.4 引用做函数返回值

作用:引用是可以作为函数的返回值存在的

注意:不要返回局部变量引用
用法:函数调用作为左值

2.5 引用的本质

本质:引用的本质在C++内部实现是一个指针常量

	int a = 10;
	//int * const ref =&a;指针常量是指针指向不可改,也说明为什么引用不可更改
	int& ref = a;
	ref = 20;//内部发现ref是引用,自动转换为*ref = 20 ;

2.6 常量引用

作用:常量引用主要用来修饰形参,防止误操作

在函数形参列表中,可以加const修饰形参,防止形参改变实参

3 函数提高

3.1 函数默认参数

3.2 函数占位参数

3.3 函数重载

可使函数名相同,提高复用性

3.3.1 函数重载概述

//满足条件
//1.同一个作用域(全局)
//2.函数名称相同
//3.函数参数类型不同,或者个数不同,或者顺序不同

//函数的返回值不可以作为函数重载的条件
void func(int a, double b)
{
}
int func(int a, double b)
{
}

3.3.2 函数重载注意事项

  • 引用作为重载条件

void func(int &a)
{
}
//int a=10,调用func(a)

int func(const int a)
{
}
//int a=10,调用func(10)

  • 函数重载碰到函数默认参数
    void func(int a,int b=5)
    {
    }
    int func(int a)
    {
    }
    func(10)//调用出现二义性,所以尽量避免函数默认参数

4 类和对象

4.1 封装

4.1.1 封装的意义

设计学生类:

#include<iostream>
using namespace std;
#include<string>

class stu
{
    
    
	//访问权限
	// 公共权限
public:
	//属性  --->成员属性  成员变量
	//姓名  
	string N_name;
	//学号
	string N_number;

	//行为  --->成员函数  成员方法
	//显示姓名和学号
	void printname()
	{
    
    
		cout << "姓名:" << N_name << "\t" << "学号:" << N_number << endl;
	}

	//赋值
	void setname(string name)
	{
    
    
		N_name = name;
	}
	void setnumber(string number)
	{
    
    
		N_number = number;
	}
};
int main()
{
    
    
	stu s1;
	//赋值方式1:
	//s1.N_name = "王飞鸿";
	//s1.N_number = "20180550";

	//赋值方式2:
	s1.setname("王飞鸿");
	s1.setnumber("20180551");

	s1.printname();

	stu s2;
	s2.setname("王大侠");
	s2.setnumber("12345678");
	s2.printname();
	
	system("pause");
	return 0;
}

访问权限

//public     公共权限  类内可以访问,类外也可以访问
//protected  保护权限  类内可以访问,类外不可以访问  儿子可以访问父亲的保护内容
//private    私有权限  类内可以访问,类外不可以访问  儿子不可以访问父亲的私有内容
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()
{
    
    
	class Person p1;
	p1.m_name = "李四";
	//p1.m_car = "摩托";
	//p1.m_password = 654321;

	system("pause");
	return 0;
}

4.1.2 struct 和class区别

//class和struct的区别
//struct 默认权限是 公共 public
//class 默认权限是 私有 private

4.1.3成员属性设置为私有

//1、可以自己控制读写权限
//2、对于写可以检测数据的有效性

class Person
{
    
    
public:
	//设置姓名
	void setName(string name)
	{
    
    
		m_name = name;
	}
	//获取姓名
	string getName()
	{
    
    
		return m_name;
	}

	//获取年龄
	int getAge()
	{
    
    
		//m_age = 0;//初始化为0岁
		return m_age;
	}

	void setAge(int age)  //2、对于写可以检测数据的有效性
	{
    
    
		if (age < 0 || age>150)
		{
    
    
			cout << "你对人的年龄有误解" << endl;
			return;
		}
		m_age = age;
	}

	//设置密码
	void setPassword(string password)
	{
    
    
		m_password = password;
	}

private:
	//姓名 可读可写
	string m_name;

	//年龄 只读
	int m_age;

	//密码 只写
	string m_password;
};
int main()
{
    
    
	Person p;
	p.setName("jhon");
	cout << "姓名:"<< p.getName() << endl;

	//p.m_name = 18;无法直接访问
	//p.getAge(18);只读状态,不能更改
	

	p.setPassword("jhon");//只能写

	p.setAge(180);
    cout << "年龄:"<< p.getAge() << endl;//若p.setAge(int age)中输入<0||>150,输出年龄会乱码

	system("pause");
	return 0;

}

案例:立方体类

class cube
{
    
    
public:
	//行为  //1
	
	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;
	}
	
	int getH()      //获取高
	{
    
    
		return m_H;
	}
	int caculateS()//获取面积 
	{
    
    
		return 2 * m_L * m_W + 2 * m_W * m_H + 2 * m_L * m_H;
	}

	int caculateV()//获取体积
	{
    
    
		return m_L * m_W * m_H;
	}
	//2.2成员函数判断立方体
	bool isSamebyclass(cube& c)
	{
    
    
		if (m_L == c.getL() && m_W == c.getW() && m_H == c.getH())
		{
    
    
			return true;
		}
		return false;
	}

private:
	//属性
	int m_L;//长
	int m_W;//宽
	int m_H;//高

};


//2.1全局函数判断立方体是否相等 //跳出class
bool isSame(cube& c1, cube& c2)
{
    
    
	if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH())
	{
    
    
		return true;
	}
	return false;
}


int main()
{
    
    
	//创建立方体对象c1
	cube c1;
	c1.setL(10);
	c1.setW(10);
	c1.setH(10);
	cout << "该立方体面积:"<<c1.caculateS() << endl;
	cout << "该立方体体积:"<<c1.caculateV() << endl;
	
	//创建第二个立方体对象c2
	cube c2;
	c2.setL(10);
	c2.setW(10);
	c2.setH(10);
	
	//全局函数判断
	bool ret= isSame(c1, c2);
	if (ret)
	{
    
    
		cout << "全局函数:c1和c2是相等的" << endl;
	}
	else
	{
    
    
        cout << "全局函数:c1和c2是不相等的" << endl;
	}
	
	//成员函数判断
	ret = c1.isSamebyclass(c2);
	if (ret)
	{
    
    
		cout << "成员函数:c1和c2是相等的" << endl;
	}
	else
	{
    
    
		cout << "成员函数:c1和c2是不相等的" << endl;
	}

	system("pause");
	return 0;
}

4.2 对象的初始化和清理

4.2.1 构造和析构函数

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无法手动调用
  • 析构函数:主要作用在于销毁前系统自动调用,执行一些清理工作

构造函数语法:类名(){}
1.构造函数,没有返回值也不写void
2.函数名称与类名相同
3.构造函数可以有参数,因此可以发生重载
4.程序在调用对象时候会自动调用构造,无法手动调用,而且只会调用一次

析构函数语法:~类名(){}
1.析构函数,没有返回值,也不用void
2.函数名称与类名相同,在名称前加上符号~
3.析构函数不可以有参数,因此不可以重载
4.程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次

4.2.2 构造函数的分类及调用

#include<iostream>
using namespace std;


class person
{
    
    
public:
//1.构造函数的分类
	//按参数分类  有参构造 无参构造(默认构造)
	//按类型构造  普通构造 拷贝构造
	
	//构造
	person()
	{
    
    
		cout << "构造函数中无参构造person的调用" << endl;
	}

	person(int a )
	{
    
    
		age = a;
		cout << "构造函数中有参构造person的调用" << endl;
	}

	person(const person &p)
	{
    
    
		age = p.age;
		cout << "构造函数中拷贝构造person的调用" << endl;
	}
	//析构
	~person()
	{
    
    
		cout << "析构函数~person的调用" << endl;
	}

	int age;
};

//2.构造函数的调用
void test01()
{
    
    
	//2.1 括号法
	person p1;
	person p2(5);
	person p3(p2);
	//注意事项1:person p1不能写成person p1(),即不能加括号,因为编译器会误认为是声明
	
	//2.2 显示法
	person p4 = person(5);
	person p5 = person(p4);
	//person(5);//单独写是匿名对象,当前行结束后马上回收,开始析构
	//注意事项2:不要利用拷贝函数匿名初始化对象,编译器会认为person (p3)===person p3(声明) 则发生"重定义"
	//person(p2);
	
	//2.3 隐式转换法
	person p6 =8;  //person p6 =person p(8)
	person p7 = p6;//person p7 =person (p6)

}



int main()
{
    
    
	
	test01(); //控制窗都显示
	//person p;   //区别构造和析构 (析构在该main函数执行完后释放~person())

	system("pause");
	return 0;
}

4.2.3 拷贝函数调用时机

三种情况:

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传递
  • 以值方式返回局部对象

4.2.4 构造函数调用规则

默认情况下,C++编译器至少给一个类添加3个函数

1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则:

  • 若用户定义有参构造,C++不再提供默认无参构造,但会提供默认拷贝构造
  • 若用户定义拷贝构造,C++不再提供其他构造函数

4.2.5 深拷贝与浅拷贝

  • 浅拷贝:简单的赋值拷贝操作
  • 深拷贝:在堆区重新申请空间进行拷贝
拷贝构造函数//new新建内存
person(const person& p)
{
    
    
	cout << "拷贝构造函数:" << endl;
	//如果不利用深拷贝在堆区新建内存,会导致浅拷贝带来的重复释放堆区问题
	m_age = p.m_age;
	m_height = new int(*p.m_height);
}
析构函数//释放new新建的内存
~person()
{
    
    
	cout << "析构函数" << endl;
	if (m_height != NULL)
	{
    
    
		delete m_height;
	}
}

4.2.6 初始化列表

作用:C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2);…{}

class person
{
    
    
public:
	person(int a, int b, int c) :m_A(a), m_B(b), m_C(c){
    
    }

	int m_A;
	int m_B;
	int m_C;
};

void print()
{
    
    
	person p(10, 20, 30);
	cout << "m_A=" << p.m_A << endl;
	cout << "m_B=" << p.m_B << endl;
	cout << "m_C=" << p.m_C << endl;
}
int main()
{
    
    
	print();

	system("pause");
	return 0;
}

4.2.7 类对象作为类成员

  • 当其他对象作为本类成员,构造时先构造类对象,再构造自身
  • 析构的顺序与构造相反
//手机类
class Phone
{
    
    
public:
	Phone(string name)
	{
    
    
		m_Pname = name;
	}
	string m_Pname;
};
//人类
class Person
{
    
    
public:
	Person(string name,string pname):m_Name(name), m_Phone(pname)
	{
    
    
	
	}
	string m_Name;
	Phone m_Phone;
};

void test01()
{
    
    
	Person p("jack", "小米");
	cout << p.m_Name << "有" << p.m_Phone.m_Pname << endl;

}
int main()
{
    
    
	test01();

	system("pause");
	return 0;
}

4.2.8 静态成员函数

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:

  • 静态成员变量
    1.所有对象共享同一份数据
    2.在编译阶段分配内存
    3.类内声明,类外初始化

  • 静态成员函数
    1.所有对象共享同一个函数
    2.静态成员函数只能访问静态成员变量

两种访问方式:
1.通过对象访问
person P;
p.func();

2.通过类名访问
person::func();

4.3 对象模型和this指针

4.3.1 成员变量和成员函数分开储存

只有非静态成员变量才属于类的对象上

4.3.2 this指针概念

this指针指向 被调用的成员函数 所属对象

用途:

  • 当形参和成员变量同名时,可用this指针来区分 this–>
  • 在类的非静态成员函数中返回对象本身(返回对象而非值),可用return *this

4.3.3 空指针访问成员函数

空指针可以访问成员,空指针不能访问属性

用以下代码避免程序崩溃

if(tihis == NULL )
{
    
    
     return}

this指针的本质:指针常量 指针的指向不可以修改

4.3.4 const修饰成员函数

常函数:
void func() const

  • 成员函数后加const后称为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以已修改

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数

4.4 友元

好基友(friend)可以访问private

4.4.1 全局函数做友元

friend void func(Person * p1);

4.4.2 类做友元

friend class GoodGay;

4.4.3 成员函数做友元

friend void GoodGay::visit();

4.5 运算符重载

4.6 继承

4.6.1 基本语法

用途:减少重复代码
语法:
class 子类:继承方式 父类;
class A :public B;

A称为子类也称为派生类
B称为父类也称为基类

//公共页面(Java、Python、C++...)
class BasePage 
{
    
    
     (公共的内容)
}
 //java页面
 class Java:public BasePage//语法
 {
    
    
 public:
    (写不一样的)
 }

4.6.2 继承方式

分三种:

  • 公共继承
  • 保护继承
  • 私有继承

4.6.3 继承中的对象模型

4.6.4 继承中构造和析构顺序

4.6.5 继承同名成员处理方式

4.6.6 继承同名静态成员处理

4.6.7 多继承语法

4.6.8 菱形继承

4.7 多态

4.7.1 多态的基本概念

多态是C++面向对象三大特性之一
多态分两类:

  • 静态多态:函数重载 和 运算符重载属于静态多态,服用函数名
  • 动态多态:派生类和虚函数实现运行时多态
    静态多态和动态多态区别:
  • 静态多态的函数地址早绑定- 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定- 运行阶段确定函数地址
    动态多态满足条件:
    1.有继承关系
    2.子类重写父类中的虚函数–>添加 virtual
    重写:函数返回类型 函数名 参数列表 完全相同

4.7.2 多态案例1计算器

4.7.3 纯虚数和抽象类

4.7.4 多态案例2制作饮品

4.7.5 虚析构和纯虚析构

4.7.6 多态案例3电脑组装

5 文件操作

未完,待续…

猜你喜欢

转载自blog.csdn.net/qq_46248455/article/details/120649101