面向对象
C++程序执行的时候,将内存大致分为四个区域:
- 代码区:写的所有代码或中文注释
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器来管理分配释放,存放函数参数值,局部变量
- 堆区:由程序员分配释放,若没释放则程序结束时由操作系统回收
意义:
不同区域存放的数据,赋予不同的生命值,给我们更大的灵活编程
程序运行前:
代码区特点:共享 只读。
全局区:该区域的数据在程序结束后由操作系统来管理释放。
C++的引用
作用:给变量起别名
语法:数据类型 &别名 = 原名
引用必须初始化,一旦初始化就不可以改变。
引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参。
优点:可以简化指针修改实参
#include <iostream>
using namespace std;
//值传递
void myswap1(int a, int b)
{
int temp = a;
a = b;
b = temp;
//cout << "值传递后 a = " << a << "值传递后 b = " << b << endl;
}
//地址传递
void myswap2(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
//cout << "地址传递后 a= " << a << "地址传递后 b= " << b << endl;
}
//引用传递
void myswap3(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
//cout << "引用传递后 a= " << a << "引用传递后 b= " << b << endl;
}
int main()
{
int a = 10, b = 20;
cout << "初始化 a: " << a << "初始化 b: " << b << endl;
myswap1(a, b);
cout << "值传递 a = " << a << "值传递 b = " << b << endl;
a = 10;
b = 20;
myswap2(&a, &b);
cout << "地址传递 a= " << a << "地址传递 b= " << b << endl;
a = 10;
b = 20;
myswap3(a, b);
cout << "引用传递 a= " << a << "引用传递 b= " << b << endl;
system("pause");
return 0;
}
引用做函数的返回值
返回静态变量引用可以用作函数左值
#include <iostream>
using namespace std;
//静态变量引用返回
int &coraline()
{
static int a = 10;
return a;
}
int main()
{
int& ref = coraline();
cout << "ref = " << ref << endl;
cout << "ref = " << ref << endl;
coraline() = 1000;//如果函数的返回值是一个引用,那么这个函数调用可以作为左值。
cout << "ref = " << ref <<endl;
cout << "ref = " << ref << endl;
system("pause");
return 0;
}
引用的本质
引用的本质在C++的内部实现是一个指针常量
相当于 int * const ref =&a;
即一旦初始化后, 就不可以发生改变。
主要使用为 int a=20;
int &ref=a;
即 ref =20;
函数高级
函数的默认参数
C++中函数的形参是可以有默认值的
函数占位参数
C++ 中函数的形参列表里可以有占位参数,用来占位,调用函数时必修填补该位置。
函数重载
作用:函数名可以相同,提高复用性。
条件:1.必须在同一个作用域下
2.函数名称相同
3.函数参数类型不同2,或者个数不同,顺序不同
注意:函数的返回值不可以做为函数重载条件
类和对象
1.封装
封装作为c++面向对象三大特性之一
封装的意义:
- 将属性和行为作为一个整体,表现生活中的事物。
- 对属性和行为加以权限设置
语法:class ***
与结构体有点相似,但是默认的访问权限不同class默认私有
struct默认权限是public
1.设计一个较为简单的类
#include <iostream>
using namespace std;
const double PI = 3.14;
class circle
{
//访问权限
public:
//属性
int m_r;
//行为(一般用函数来设置)
double calculate()
{
return 2 * PI * m_r;
}
};
int main()
{
//通过 class circle 来创建一个具体的圆(对象),并且赋值
circle c1;
c1.m_r = 10;
cout << "圆的周长: " << c1.calculate() << endl;
system("pause");
return 0;
}
//总结:我们通过圆类来创建一个对象,然后对于对象的属性进行赋值,访问对象的行为,获取对象的内容。
//实例化:通过一个类来创建一个对象的过程。
2.访问权限
三种访问权限
- 公共权限: 成员 类内可以访问 类外可以访问
- 保护权限: 成员 类内可以访问 类外不可访问(与私有权限区别在于继承里面)
- 私有权限: 成员 类内可以访问 类外不可访问
#include <iostream>
using namespace std;
#include <string>
class person
{
public:
string m_name;
protected:
string m_car;
private:
int m_password;
public:
void func()
{
m_name = "coraline";
m_car = "bus";
m_password = 123456;
}
};
int main()
{
person p1;
p1.m_name = "hh";//只可以访问名字,因为权限设置是public。
system("pause");
return 0;
}
3.成员属性私有化(设置读写状态)
#include <iostream>
using namespace std;
#include <string>
//设置私有可以控制自己的读写权限
class person
{
public:
//先将属性全部私有化,然后分开设置
//设置名字为可读可写
void setname(string name)
{
m_name = name;
}
string getname()
{
return m_name;
}
//设置年龄可读可写
void setage(int age)
{
if (age < 0 || age >150)
{
cout << "你这个老妖怪" << endl;
return;
}
m_age = age;
}
int getage()
{
m_age = 0;
return m_age ;
}
//设置lover可写
void setlover(string lover)
{
m_lover = lover;
}
private:
string m_name;
int m_age;
string m_lover;
};
int main()
{
person p;
p.setname("coraline"); //写
cout << "姓名: " << p.getname() << endl; //读
p.setage(100); //写
cout << "年龄: " << p.getage() << endl; //读
p.setlover("taylor"); //不可访问只能设置。
system("pause");
return 0;
}
4.对象设置
构造函数:作用在于创造对象时为对象的属性赋值,编译器自动调用
- 没有返回值也不写void
- 函数名与类名相同
- 构造函数可以有参数,因此可以发生重载
- 只会调用一次
析构函数:对象销毁前系统自动调用,执行清理作用。
- 没有返回值也不写void
- 函数名与类名相同,在名称前面加~
- 只会调用一次
#include <iostream>
using namespace std;
#include <string>
class person
{
//构造函数
public:
person()
{
cout << "person 构造函数的调用" << endl;
}
//析构函数(对象销毁了才会调用)
~person()
{
cout << "person 析构函数的调用" << endl;
}
};
//构造和析构都是必须有的实现
void test01()
{
person p;//局部变量在栈上,执行完毕后会释放。
}
int main()
{
test01();
system("pause");
return 0;
}
5.构造函数的分类以及调用
构造函数
按参数分类:有参构造 无参构造(默认构造函数)
按类型分类: 普通构造函数 拷贝构造函数
#include <iostream>
using namespace std;
#include <string>
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 test01()
{
//括号法
person p;//默认构造函数的调用。
person p2(10);//有参构造函数。
person p3(p2);//拷贝构造函数调用。
//显示法
person p;
person p2 = person(10);//有参构造
person p3 = person(p2);//拷贝构造
//隐式转换法
person p4 = 10;//相当于person p4 = person (10);有参构造
person p5 = p4;//拷贝构造
}
int main()
{
test01();
system("pause");
return 0;
}
上图为几种定义与调用的方法
拷贝构造函数的调用场景
- 用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传递
- 以值方式返回局部函数
6.构造函数的调用规则
每当我们创建一个类的时候,C++ 编译器至少给一个类添加3个函数
默认构造函数,析构函数,拷贝函数(对属性进行值拷贝)
调用规则:
若用户定义有参构造函数,C++会提供默认拷贝函数(不提供无参构造)
若用户定义拷贝构造函数,C++不会再调用其他构造函数
7.深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间进行拷贝操作。
注意:如果属性又在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
释放的顺序是先进后出,所以P2会先被释放,而P2访问的是P1的拷贝构造,但是被释放了,P1就不能对NULL进行访问释放。
#include<iostream>
using namespace std;
class person
{
public:
person()
{
cout << "person 的默认构造函数调用" << endl;
}
person(int age , int height)
{
m_age = age;
m_height = new int (height);
cout << "person的有参构造函数调用" << endl;
}
//自己实现拷贝函数,解决浅拷贝带来的问题
person(const person& p)
{
cout << "拷贝构造函数的调用" << endl;
m_age = p.m_age;
//m_height = p.m_height;(这行是编译器默认的实现代码)
//下面进行深拷贝操作
m_height = new int(*p.m_height);//让自己的这个指向新的内存。
}
~person()
{
//析构代码,将堆区开辟的数据做释放操作
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "person的析构函数调用" << endl;
}
int m_age;
int *m_height;//加一个指针指向身高(把这个开辟到堆区)
};
void test01()
{
person p1(18,160);
cout << "p1的年龄为: " << p1.m_age << "身高为: "<<*p1.m_height <<endl;
person p2(p1);
cout << "p2的年龄为: " << p1.m_age << "身高为: " << *p2.m_height << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
8.初始化列表
作用:对类中的属性进行初始化操作
9.类对象作为类成员
C++类中的成员可以是另一个类的对象,这个成员被称作对象成员
有点类似与C语言中的结构体嵌套结构体
10.静态成员
静态成员分类
1.静态成员变量
所有对象共享一份数据
在编译阶段分配内存,类内声明,类外初始化
2.静态成员函数
所有对象共享同一个函数
静态成员只能访问静态成员变量
C++对象模型和this指针
1.成员变量和成员函数分开储存
在C++中,类的成员变量和成员函数分开储存,只有非静态成员变量才属于类的对象上。