目录
c++面向对象三大特性:封装、继承、多态。
6.1 封装
6.1.1 封装的意义
1. 将属性和行为作为一个整体。(放在一个class里面)
2. 将属性和行为加以权限控制。
public公共权限:类内外都可以访问
protected保护权限: 类外不可以访问
private私有权限: 类外不可以访问
//定义一个圆类
#define PI 3.14
//class 定义一个类 circle是类的名字
class circle
{
//访问权限:公共权限
public:
//属性
int r;
//行为
double circumference( )
{
return r * PI * 2;
}
};
int main()
{
circle c1;//创建具体的圆(对象)(实例化)
c1.r = 10;//给具体的圆的属性赋值
cout << "圆的周长为:" << c1.circumference() << endl;
return 0;
}
6.1.2 struct和class的区别
struct
默认权限为: 共有 public
class
默认权限为: 私有 private
6.1.3 将成员属性设为私有
优点:
①将成员属性设为私有,可以自己控制读写的权限。
②对于写权限,可以检测数据的有效性。
class person
{
private:
string name;//可读写
int age;//只读
string lover;//只写
public:
void SetName(string s)
{
name = s;
}
string GetName()
{
return name;
}
int GetAge()
{
age = 18;
return age;
}
void SetLover(string s)
{
lover = s;
}
};
int main()
{
person p1;
p1.SetName("xiyang");
p1.SetLover("2A");
cout << "姓名为:" << p1.GetName() << endl;
cout << "年龄为:" << p1.GetAge() << endl;
return 0;
}
设计案例1:立方体类
/*
要求:
1.设计一个立方体类
2.求出立方体的面积和体积
3.分别用全局函数和成员函数判断两个立方体是否相等
*/
class cube
{
private:
//属性
int L;
int W;
int H;
public:
//行为
//设置 获取长,宽,高
void SetL(int a)
{
L = a;
}
int GetL()
{
return L;
}
void SetW(int a)
{
W = a;
}
int GetW()
{
return W;
}
void SetH(int a)
{
H = a;
}
int GetH()
{
return H;
}
//获得面积
int S()
{
return 2 * ((L * W) + (L * H) + (W * H));
}
//获得体积
int V()
{
return L * W * H;
}
//成员函数判断
bool isSameByClass(cube& c)
{
if (c.GetL() == L && c.GetW() ==W && c.GetH() == H)
return true;
else
return false;
}
};
//全局函数判断
bool isSame(cube& c1, cube& c2)
{
if (c1.GetL() == c2.GetL() && c1.GetW() == c2.GetW() && c1.GetH() == c2.GetH())
return true;
else
return false;
}
int main()
{
cube c1,c2;
c1.SetL(10);
c1.SetW(10);
c1.SetH(10);
c2.SetL(10);
c2.SetW(10);
c2.SetH(10);
cout << "第一个立方体的面积为:" << c1.S() << endl;
cout << "第一个立方体的体积为:" << c1.V() << endl;
bool ret1 = isSame(c1, c2);
if (ret1)
{
cout << "全局函数判断c1 c2相等" << endl;
}
else
cout << "全局函数判断c1 c2不相等" << endl;
bool ret2 = c1.isSameByClass(c2);
if (ret2)
{
cout << "成员函数判断c1 c2相等" << endl;
}
else
cout << "成员函数判断c1 c2不相等" << endl;
return 0;
}
设计案例2:点和圆的关系
源.cpp
#include<iostream>
using namespace std;
#include"point.h"
#include"circle.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;
Point center;
Point q;
center.setX(10);
center.setY(10);
q.setX(10);
q.setY(10);
c.setCenter(center);
c.setR(10);
IsInCircle(c, q);
return 0;
}
circle.h
#pragma once
#include<iostream>
using namespace std;
#include"point.h"
class Circle {
public:
void setR(int r);
int getR();
void setCenter(Point center);
Point getCenter();
private:
int m_R;
Point m_Center;
};
circle.cpp
#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.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;//д
};
point.cpp
#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;
}
6.2 对象的初始化和清理
C++利用构造函数和析构函数解决了对象的初始化和清理。对象的初始化和清理工作是编译器强制要求我们做的事情,因此就算我们不提供构造和析构,编译器也会提供,只不过编译器提供的是构造函数和析构函数的空实现。
6.2.1 构造函数
定义:主要作用在创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
语法:类名
()
{ }
注意:①没有返回值,不用写void。
②函数名与类名相同。
③构造函数可以有参数,可以发生重载。
④创建对象时,构造函数会自动调用,并且只调用一次。
分类:
按参数分类:有参构造和无参构造(默认构造)
按类型分类:普通构造和拷贝构造
调用方式:
括号法、显示法、隐式转换法
//构造的分类和调用
class person
{
public:
//无参(普通构造)(默认构造)
person()
{
cout << "无参构造函数调用" << endl;
}
//有参(普通构造)
person(int a)
{
age = a;
cout << "有参构造函数调用" << endl;
}
//拷贝构造函数
person(const person& p) //加const作用,拷贝之后防止本体被修改
{
age = p.age;//克隆数据
cout << "拷贝构造函数的调用" << endl;
}
int age;
};
int main()
{
//括号法
person p1;//不能加(),加了()编译器会认为是一个函数声明,在一个函数体里可以声明另一个函数
person p2(10); //p2的年龄初始化为10
person p3(p2);
//显示法
person p4 = person();
person p5 = person(10);
person p6 = person(p5);
//person()为匿名对象,没有名字,但创建了对象,特点:当前行执行结束后系统就会回收掉匿名对象
//不要用拷贝构造函数初始化匿名对象,如person(p3),等价于person p3
//隐式转换法
person p7 = 10;//转换为:person p7=person(10)
}
6.2.2 析构函数
定义:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
语法:~类名
()
{ }
注意:①没有返回值,不写void
②函数名和类名相同,在名称前加~
③析构函数不可以有参数,不可以发生重载
④对象在销毁前会自动调用析构函数,并且只会调用一次
//构造函数和析构函数例子
class person
{
public:
person()
{
cout << "构造函数的调用" << endl;
}
~person()
{
cout << "析构函数的调用" << endl;
}
};
void test()
{
person P;
}
int main()
{
test();
return 0;
}
6.2.3 拷贝构造函数调用时机
C++中拷贝函数调用一般有三种情况:
①使用一个已创建完毕的对象来初始化一个新对象。
②值传递的方式给函数参数传值。
③以值的方式返回局部对象。
class person
{
public:
person()
{
cout << "默认构造函数的调用" << endl;
}
person(int a)
{
age = a;
cout << "默认构造函数的调用" << endl;
}
person(const person& p)
{
age = p.age;//克隆数据
cout << "拷贝构造函数的调用" << endl;
}
int show()
{
return age;
}
private:
int age;
};
void test1(person p)
{
}
person test2()
{
person p;
return p;
}
int main()
{
//使用一个已创建完毕的对象来初始化一个新对象
person p1(20);
person p2(p1);
cout << "p2的年龄为:" << p2.show() << endl;
//值传递的方式给函数参数传值,拷贝一份临时副本作为形参,改变临时副本的值不影响实参
person p3;
test1(p3);
//以值的方式返回局部对象,return返回时返回的不是局部对象,而是拷贝一个新对象返回
test2();
return 0;
}
6.2.4 构造函数调用规则
创建一个类,C++至少给每一个类添加4个函数:
1、默认构造(空实现)
2、析构函数(空实现)
3、拷贝构造(值拷贝)
4、赋值运算符Operator=对属性进行值拷贝
①如果用户定义一个有参构造函数,C++不会提供默认构造函数,但是会提供拷贝构造函数。
②如果用户定义一个拷贝构造函数,C++不会提供别的构造函数。
//eg.①如果用户定义一个有参构造函数,C++不会提供默认构造函数,但是会提供拷贝构造函数
class person
{
public:
person(int a)
{
age = a;
cout << "默认构造函数的调用" << endl;
}
int show()
{
return age;
}
private:
int age;
};
int main()
{
person p1;//err
person p2(18);
person p3(p2);//拷贝构造函数
cout << "p2的年龄为:" << p2.show() << endl;
return 0;
}
//②如果用户定义一个拷贝构造函数,C++不会提供别的构造函数
class person
{
public:
person(const person& p)
{
age = p.age;//克隆数据
cout << "拷贝构造函数的调用" << endl;
}
int show()
{
return age;
}
private:
int age;
};
int main()
{
person p1;//err
person p2(18);//err
person p3(p1);
cout << "p2的年龄为:" << p2.show() << endl;
return 0;
}
6.2.5 深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝。
深拷贝:在堆区重新申请空间,进行拷贝。
浅拷贝存在的问题:属性有在堆区开辟的,拷贝之后两个类成员堆区属性都指向同一个地址,新拷贝成员执行析构操作之