C++面向对象的三大特性:封装,继承,多态
万事万物皆可为对象,有其相应的属性和行为
一、封装
1.1 封装的意义
·将属性和行为作为一个整体,表现生活中的事物
·将属性和行为加以权限控制
·在设计类的时候,属性和行为写在一起,表现事物
1.2 语法
class 类名{ 访问权限:属性 / 行为};
eg1.设计一个圆类
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
//设计一个圆类,求圆的周长:2*PI*半径
const double PI = 3.14;
//class代表一个类,类后面紧跟就是类名称
class Circle {
//访问权限
public://公共权限
//属性
int m_r;//半径
//行为,通常用函数
double calculateZC() {
return 2 * PI * m_r;
}
};
int main() {
//通过圆类 创建具体的圆
//实例化 (通过一个类 创建一个对象的过程)
Circle cl;
//给圆对象的属性进行赋值
cl.m_r = 10;
cout << "圆的周长:" << cl.calculateZC() << endl;
system("pause");
return 0;
}
eg2.设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,也可以显示
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
class Student {
//类中的属性和行为都属于 成员
//属性 成员属性 成员变量
//行为 成员函数 成员方法
public:
//属性
string m_Name;//姓名
int m_id;//学号
//行为
void showStudent() {
cout << "姓名:" << m_Name << "学号:" << m_id << endl;
}
//给姓名赋值
void setName(string name) {
m_Name = name;
}
//给学号赋值
void setId(int id) {
m_id = id;
}
};
int main() {
//实例化
Student s1;
//赋值
s1.m_Name = "张三";
s1.m_id = 123;
s1.showStudent();
Student s2;
s2.setName("李四");//通过函数赋值
s2.setId(321);
s2.showStudent();
system("pause");
return 0;
}
1.3 访问权限
类在设计时,可以把属性和行为放在不同的权限下加以控制
1.public 公共权限 成员类内可以访问,类外可以访问
2.protected 保护权限 成员类内可以访问,类外不可以访问,可以继承
3.private 私有权限 成员类内可以访问,类外不可以访问,不可以继承
eg.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
class Persson {
public:
//公共属性
string m_Name;
protected:
//保护权限
string m_Car;
private:
//私有权限
int m_Password;
public:
void func() {
m_Name = "张三";
m_Car = "拖拉机";
m_Password = 123456;//这里类内都可以访问
}
};
int main() {
//实例化
Persson p1;
p1.m_Name = "李四";
//p1.m_Car = "奔驰";//保护权限内容,类外无法访问
//p1.m_Password = 132;//私有权限内容,类外无法访问
p1.func();
system("pause");
return 0;
}
1.4 struct和class区别
在C++中struct和class唯一的区别在于 默认的访问权限不同
·struct默认权限为公共
·class默认权限为私有
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
class C1 {
int m_A;//默认权限 私有
};
struct C2 {
int m_A;//默认权限 公共
};
int main() {
//struct 默认权限为公共
//class 默认权限为私有
C1 c1;
//c1.m_A = 100;//会报错的,类外无法访问
C2 c2;
c2.m_A = 100;//成功
system("pause");
return 0;
}
1.5 成员属性设置为私有
优点1:将所有成员设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
//姓名的设置
void setName(string name) {
m_Name = name;
}
string getName() {
return m_Name;
}
void setAge(int age) {
//检测优点2的,与开始只读不冲突哈
if (age < 0 || age>100) {
cout << "年龄"<<age<<"有误" << endl;
return;
}
m_Age = age;
}
int getAge() {
return m_Age;
}
void SetIdol(string idol) {
m_Idol = idol;
}
private:
string m_Name;//姓名 可读可写
int m_Age = 18;//年龄 只读 也可以写在0~100之间
string m_Idol;//偶像 只写
};
int main() {
Person p;
//姓名
p.setName("张三");
cout << "姓名:" << p.getName() << endl;
//年龄
//p.setAge(20);//报错
//p.m_Age = 20;//报错
//后面加的
p.setAge(15);
cout << "年龄:" << p.getAge() << endl;
//偶像
p.SetIdol("小米");
//cout << "偶像:" << p.SetIdol() << endl;//报错,只写状态
system("pause");
return 0;
}
eg1.设计立方体类,求出其面积和体积,分别用全局函数和成员函数判断两个立方体是否相等
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
//立方体类的设计
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;
}
int getH() {
return m_H;
}
//获取面积
int calculateS() {
return 2 * m_L * m_W + 2 * m_L * m_H + 2 * m_H * m_W;
}
//获取体积
int calculateV() {
return m_L * m_W * m_H;
}
//成员函数判断两立方体是否相等
bool isSameByClass(Cube &c) {
if (m_L == c.getL() && m_H == c.getH() && m_W == c.getW()) {
return true;
}
else {
return false;
}
}
private:
int m_L;
int m_H;
int m_W;
};
//利用全局函数判断 两个立方体是否相等
bool isSame(Cube& c1, Cube& c2) {
if (c1.getH() == c2.getH() && c1.getL() == c2.getL() && c1.getW() == c2.getW()) {
return true;
}
return false;
}
int main() {
Cube c1;
c1.setH(10);
c1.setL(10);
c1.setW(10);
cout << "c1的面积为:" << c1.calculateS() << endl;
cout << "c1的体积为:" << c1.calculateV() << endl;
Cube c2;
c2.setH(10);
c2.setL(10);
c2.setW(10);
//利用全局函数判断
bool ret = isSame(c1, c2);
if (ret) {
cout << "全局函数判断相等" << endl;
}
else {
cout << "全局函数判断不想等" << endl;
}
//利用成员函数判断
ret = c1.isSameByClass(c2);
if (ret) {
cout << "成员函数判断相等" << endl;
}
else {
cout << "成员函数判断不想等" << endl;
}
system("pause");
return 0;
}
eg2.点和圆的关系,设计圆类和点类
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
class Point {
public:
void setX(int x) {
m_X = x;
}
int getX() {
return m_X;
}
void setY(int y) {
m_Y = y;
}
int getY() {
return m_Y;
}
private:
int m_X;
int m_Y;
};
class Circle {
public:
void setR(int r) {
m_R = r;
}
int getR() {
return m_R;
}
void setCenter(Point center) {
m_Center = center;
}
Point getCenter() {
return m_Center;
}
private:
int m_R;
//在一个类中可以让另一个类 作为本来中的成员
Point m_Center;
};
//判断点和圆的关系
void isInCircle(Circle& c, Point& p) {
//点和圆心的距离
int distance = (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
//计算半径的平方
int rDistance = c.getR() * c.getR();
if (distance == rDistance) {
cout << "点在圆上" << endl;
}
else if (distance > rDistance) {
cout << "点在圆外" << endl;
}
else {
cout << "点在圆内" << endl;
}
}
int main() {
Circle c;
c.setR(10);
Point center;
center.setX(10);
center.setY(0);
c.setCenter(center);
Point p;
p.setX(10);
p.setY(11);
isInCircle(c, p);
system("pause");
return 0;
}
在这里我们把两个类分开写,写法如下:
文件包括
其中头文件
circle.h
#pragma once
#include <iostream>
#include "point.h"
using namespace std;
class Circle {
public:
void setR(int r);
int getR();
void setCenter(Point center);
Point getCenter();
private:
int m_R;
Point m_Center;
};
point.h
pragma once //防止头文件重复包含
#include <iostream>
using namespace std;
class Point {
public:
void setX(int x);
int getX();
void setY(int y);
int getY();
private:
int m_X;
int m_Y;
};
circle.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "circle.h"
void Circle::setR(int r) {
m_R = r;
}
int Circle::getR() {
return m_R;
}
void Circle::setCenter(Point center) {
m_Center = center;
}
Point Circle::getCenter() {
return m_Center;
}
point.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "point.h"
void Point::setX(int x) {
m_X = x;
}
int Point::getX() {
return m_X;
}
void Point::setY(int y) {
m_Y = y;
}
int Point::getY() {
return m_Y;
}
run.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;
#include "circle.h"
#include "point.h"
//判断点和圆的关系
void isInCircle(Circle& c, Point& p) {
//点和圆心的距离
int distance = (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
//计算半径的平方
int rDistance = c.getR() * c.getR();
if (distance == rDistance) {
cout << "点在圆上" << endl;
}
else if (distance > rDistance) {
cout << "点在圆外" << endl;
}
else {
cout << "点在圆内" << endl;
}
}
int main() {
Circle c;
c.setR(10);
Point center;
center.setX(10);
center.setY(0);
c.setCenter(center);
Point p;
p.setX(10);
p.setY(11);
isInCircle(c, p);
system("pause");
return 0;
}
头文件只有声明函数,相对应的文件实现函数功能,注意加上类的作用域
二、对象的初始化和清理
2.1 构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其只用后果是未知的,同样的使用完后不清理,也会造成安全问题
C++利用构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象的初始化和清理工作。是强制要我们做的,如果我们不提供构造函数和析构函数,编译器会提供空实现的函数。
·构造函数:主要作用于创建对象时为对象的成员赋值,构造函数由编译器自动调用
·析构函数:主要作用于在对象销毁前系统自动调用,执行一些清理功能
构造函数语法:类名(){}
1.构造函数,没有返回值也不写void
2.函数名称与类名相同
3.构造函数可以有参数,因此可以发生重载
4.程序在调用对象时候会自动调用一次构造函数
析构函数语法:~类名(){}
1.析构函数,没有返回值也不写void
2.函数名称与类名相同,在名称前加~
3.构造函数不可以有参数,因此不可以发生重载
4..程序在销毁对象前会自动调用一次析构函数
eg.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person {
public:
//构造函数,可以有参数
Person() {
cout << "Person 构造函数的调用" << endl;
}
//析构函数
~Person() {
cout << "Person 析构函数的调用" << endl;
}
};
void test01() {
Person p;//创建在栈上,test01执行完毕后,会释放这个对象
}
int main() {
test01();
Person q;//这里只有创建,没有释放
system("pause");//在这里按下时才会释放q
return 0;
}
2.2 构造函数的分类及调用
两种分类方式:
按参数分为:有参构造和无参构造 无参又称为默认构造函数
按类型分为:普通构造和拷贝构造
三中调用方式:
括号法、显示法、隐式转换法
eg.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person {
public:
//无参构造(普通构造)
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;
};
//调用
void test01() {
//1.括号法
Person p1;//默认构造函数的调用
Person p2(10);//有参构造函数
Person p3(p2);//拷贝构造函数
//注意事项
// 调用默认构造函数的时候,不要加() Person p1();会认为是一个函数的声明
//cout << "p2的年龄:" << p2.age << endl;
//cout << "p3的年龄:" << p3.age << endl;
//2.显示法
Person p4;
Person p5 = Person(10);//有参构造
Person p6 = Person(p2);//拷贝构造
//注意事项:
//Person(10);匿名对象 特点:当前行执行结束后,系统会立即回收掉
//所以你单独写时,会调用构造函数,并立刻销毁
//不要利用拷贝构造函数初始化匿名对象 Person(p3) 编译器会认为Person(p3) == Person p3;
//3.隐式转换法
Person p7 = 10;//相当于 写了 Person p7 = Person(10);有参构造
Person p8 = p7;//拷贝构造
}
int main() {
test01();
system("pause");
return 0;
}
2.3 拷贝构造函数调用的时机
1.使用一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式给构造函数参数传值
3.以值方式返回局部对象(注意新版VS中已经不再适用,不会再调用,两地址相同了)
eg.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person {
public:
Person() {
cout << "Person默认构造函数调用" << endl;
}
Person(int age) {
cout << "Person有参构造函数调用" << endl;
m_Age = age;
}
Person(const Person& p) {
cout << "Person拷贝构造函数调用" << endl;
m_Age = p.m_Age;
}
~Person() {
cout << "Person析构函数调用" << endl;
}
int m_Age;
};
//1.使用一个已经创建完毕的对象来初始化一个新对象
void test01() {
Person p1(20);
Person p2(p1);
cout << "p2的年龄为:" << p2.m_Age << endl;
}
//2.值传递的方式给构造函数参数传值
void doWork(Person p) {
//test02的p传给了这里的p就进行了拷贝
}
void test02() {
Person p;
doWork(p);
}
//3.以值方式返回局部对象,注意新版VS中已经不再适用,不会再调用,两地址相同了
Person doWork2() {
Person p1;
cout << (int*)&p1 << endl;
return p1;
}
void test03() {
Person p = doWork2();
cout << (int*)&p << endl;
}
int main() {
//test01();
//test02();
test03();
system("pause");
return 0;
}
2.4 构造函数的调用规则
1.默认构造函数(无参,函数体为空)
2..默认析构函数(无参,函数体为空)
3..默认拷贝构造函数,对属性值进行值拷贝
构造函数调用规则如下:
1.如果用户定义有参构造函数,c++不再提供默认无参构造,但是会提供默认拷贝构造函数
2.如果用户定义默认拷贝构造,c++不会提供其他构造函数
eg.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person {
public:
Person() {
cout << "Person默认构造函数调用" << endl;
}
Person(int age) {
cout << "Person有参构造函数调用" << endl;
m_Age = age;
}
Person(const Person& p) {
cout << "Person拷贝构造函数调用" << endl;
m_Age = p.m_Age;
}
~Person() {
cout << "Person析构函数调用" << endl;
}
int m_Age;
};
void test01() {
Person p;
p.m_Age = 18;
Person p2(p);
cout << "p2的年龄为:" << p2.m_Age << endl;
}
int main() {
//test01();//如果不写拷贝构造,就会实现拷贝构造的空实现
//如果用户定义有参构造函数,c++不再提供默认无参构造
//如果用户定义默认拷贝构造,c++不会提供其他构造函数
system("pause");
return 0;
}
2.5 深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间进行拷贝操作
浅拷贝带来的问题
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person {
public:
Person() {
cout << "Person默认构造函数调用" << endl;
}
Person(int age,int height) {
cout << "Person有参构造函数调用" << endl;
m_Age = age;
m_Height = new int(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的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
如上面代码所示,我并没有写拷贝构造,而是编译器自己进行默认拷贝,即为浅拷贝,在int m_Height上没有问题,但是在指针变量int *m_Height上,因为是在堆区上创建的,在该堆区地址上进行赋值,p1和p2指的是同一个地址,而p1,p2入栈后,p2先出来释放了该堆区地址,p1出来再次释放,从而导致错误
所以要利用深拷贝进行解决,即在堆区新开辟一个空间,代码如下
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person {
public:
Person() {
cout << "Person默认构造函数调用" << endl;
}
Person(int age,int height) {
cout << "Person有参构造函数调用" << endl;
m_Age = age;
m_Height = new int(height);
}
Person(const Person &p) {
cout << "Person拷贝构造函数调用" << 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的年龄为:" << p2.m_Age << "身高为:" << *p2.m_Height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
2.6 初始化列表
作用:
c++提供了初始化列表语法,用来初始化属性
语法:
构造函数():属性1(值1),属性2(值2)...{}
eg.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person {
public:
//传统初始化
/*Person(int a,int b,int c) {
m_A = a;
m_B = b;
m_C = c;
}*/
//初始化列表初始化属性
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 test01() {
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() {
test01();
system("pause");
return 0;
}
2.7 类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称为 对象成员
class A{};
class B{
A a;
};
B类中有对象A作为成员,A为对象成员,那么创建B的时候,A与B的构造与析构顺序?
显然是A。当其他类对象作为本类成员,构造时候先构造类对象,再构造自身,析构入栈顺序不变,先进去类对象再进去自身,所以自身先出来,类对象后出来,看上去顺序相反
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;
class Phone {
public:
Phone(string pName) {
m_PName = pName;
cout << "Phone构造函数调用" << endl;
}
~Phone() {
cout << "Phone析构函数调用" << endl;
}
string m_PName;
};
class Person {
public:
//Phone m_Phone = pName;隐式转化法
Person(string name, string pName):m_Name(name),m_Phone(pName) {
cout << "Person构造函数调用" << endl;
}
~Person(){
cout << "Person析构函数调用" << endl;
}
string m_Name;
Phone m_Phone;
};
void test01() {
Person p("张三", "华为");
cout << p.m_Name << "拿着" << p.m_Phone.m_PName << endl;
}
int main() {
test01();
system("pause");
return 0;
}
2.8 静态成员
静态成员就是在成员函数前加上关键字static,称为静态成员
静态成员分为:
1.静态成员变量
1.1所有对象共享同一份数据
1.2在编译阶段分配内存
1.3类内声明,类外初始化
2.静态成员函数:
2.1所有对象共享同一函数
2.2静态成员函数只能访问静态成员变量
eg静态成员变量.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;
class Person {
public:
//1 所有对象都共享同一份数据
//2 编译阶段就分配内存
//3 类内声明,类外初始化操作
static int m_A;
//静态成员变量也是有访问权限的
private:
static int m_B;
};
int Person::m_A = 100;
int Person::m_B = 200;
void test01() {
Person p;
cout << p.m_A << endl;
Person p2;
p2.m_A = 200;
cout << p.m_A << endl;
}
void test02() {
//静态成员变量,不属于某个对象上,所有对象都共享同一份数据
//因此静态成员变量有两种访问方式
//1.通过对象访问
//Person p;
//cout << p.m_A << endl;
//2.通过类名访问
cout << Person::m_A << endl;
//cout << Person::m_B << endl;类外不可访问私有变量
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
eg静态成员函数.
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;
class Person {
//静态成员函数只能访问静态成员变量
public:
static void func() {
m_A = 100;
//m_B = 200;会报错 静态成员函数 不可以访问 非静态成员变量
//无法区分到底是哪个对象的m_B属性
cout << "static void func调用" << endl;
}
static int m_A;
int m_B;
//静态成员函数也是有访问权限的
private:
static void func() {
m_A = 200;
//m_B = 200;会报错 静态成员函数 不可以访问 非静态成员变量
//无法区分到底是哪个对象的m_B属性
cout << "static void func2调用" << endl;
}
};
int Person::m_A = 0;
void test01() {
//1、通过对象访问
//Person p;
//p.func();
//2、通过类名访问
Person::func();
//Person::func2();类外无法访问
}
int main() {
test01();
system("pause");
return 0;
}