一、类的引出概述
在c语言结构体中,行为和属性是分开的,万一调用错误,将会导致问题发生。c++中类将数据和方法封装在一起,加以权限区分,用户只能通过公共方法 访问 私有数据。
二、封装
封装特性包含两个方面,一个是属性和变量合成一个整体,一个是给属性和函数增加访问权限。
1.把变量(属性)和函数(操作)合成一个整体,封装在一个类中
2.对变量和函数进行访问控制访问权限
3.在类的内部(作用域范围内),没有访问权限之分,所有成员可以相互访问4.在类的外部(作用域范围外),访问权限才有意义:.public、private、protected
5.在类的外部,只有public.修饰的成员太能被访问,在没有涉及继承与派生时,private和protected是相同等级的.外部不允许访问
访问属性 | 属性 | 对象内部 | 对象外部 |
public | 公有 | 可访问 | 可访问 |
private | 保护 | 可访问 | 不可访问 |
protected | 私有 | 可访问 | 不可访问 |
尽量设置成员变量为私有权限,将方法设置成公有。
优点:
对变量的设置时的控制
实现变量设置只读权限
实现变量设置只写权限
实现变量设置可读可写权限
struct和class的区别:class默认访问权限为.private,struct默认访问权限为public.
三、类的案例(了解)
1、设计一个person类
设计一个Person类,Person类具有name和age属性,提供初始化函数(Init),并提供对name和age 的读写函数(set,get),但必须确保age的赋值在有效范围内(0-100),超出有效范围,则拒绝赋值,并 提供方法输出姓名和年龄。----需要六个函数
#include <iostream>
using namespace std;
#include<string.h>
class Person
{
private:char mName[32];int mAge;
public://初始化成员void init(char *name, int age){strcpy(mName, name);if(age>=0 && age<=100){mAge = age;}else{cout<<"年龄无效"<<endl;}return;}//设置namevoid setName(char *name){strcpy(mName, name);}//获取namechar *getName(void){return mName;}//设置agevoid setAge(int age){if(age>=0 && age<=100){mAge = age;}else{cout<<"年龄无效"<<endl;}}//得到ageint getAge(void){return mAge;}//显示所有数据void showPerson(void){cout<<mName<<" "<<mAge<<endl;}
};
void test02()
{Person ob1;ob1.init("lucy", 18);ob1.showPerson();ob1.setName("bob");cout<<"年龄:"<<ob1.getAge()<<endl;ob1.showPerson();
}
2、设计一个立方体类
设计立方体类(Cube),求出立方体的面积( 2ab +2ac +2bc )和体积( a*b*c),分别用全局函数和成员函数判断两个立方体是否相等。
class Cube
{
private:int mA;int mB;int mC;public:void setA(int a){mA = a;}int getA(void){return mA;}void setB(int b){mB = b;}int getB(void){return mB;}void setC(int c){mC = c;}int getC(void){return mC;}//获取面积int getS(void){return (mA*mB+mB*mC+mC*mA)*2;}//获取体积int getV(void){return mA*mB*mC;}//成员函数 比较两个立方体是否先等bool compareCube01(Cube &ob){if(mA==ob.mA && mB ==ob.mB && mC == ob.mC){return true;}return false;}
};//全局函数 比较两个立方体是否先等
bool compareCube02(Cube &ob1, Cube &ob2)
{if(ob1.getA()==ob2.getA() && ob1.getB() ==ob2.getB() && ob1.getC() == ob2.getC()){return true;}return false;
}void test()
{Cube ob1;ob1.setA(10);ob1.setB(20);ob1.setC(30);cout<<"面积:"<<ob1.getS()<<endl;cout<<"体积:"<<ob1.getV()<<endl;Cube ob2;ob2.setA(10);ob2.setB(20);ob2.setC(30);// if(compareCube02(ob1, ob2))if(ob1.compareCube01(ob2)){cout<<"相等"<<endl;}else{cout<<"不相等"<<endl;}
}
int main(int argc, char *argv[])
{test();return 0;
}
3、点和圆的关系
设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。
class Point
{
private:int mX;int mY;
public:void setX(int x){mX = x;}int getX(void){return mX;}void setY(int y){mY = y;}int getY(void){return mY;}
};
class Circle
{
private:Point p;//对象作为类的成员变量int mR;
public:void setPoint(int x, int y){p.setX(x);p.setY(y);}Point getPoint(void)//打印圆点{return p;}void setR(int r){mR = r;}int getR(void){return mR;}//判断点 在圆的位置int pointIsOnCircle(Point &ob){int len = (ob.getX()-p.getX())*(ob.getX()-p.getX())+\(ob.getY()-p.getY())*(ob.getY()-p.getY());if(len == mR*mR){return 0;}else if(len > mR*mR){return 1;}else if(len < mR*mR){return -1;}}
};
void test()
{
//实例化一个点的对象Point p;p.setX(5);p.setY(5);//实例化一个圆的对象Circle cir;cir.setPoint(2,2);cir.setR(5);if(cir.pointIsOnCircle(p) == 0){cout<<"点在圆上"<<endl;}else if(cir.pointIsOnCircle(p) > 0){cout<<"点在圆外"<<endl;}else if(cir.pointIsOnCircle(p) < 0){cout<<"点在圆内"<<endl;}
}
4、类中成员函数在类外实现
5、类在其他文件实现
头文件定义类, cpp实现类的成员函数
data.h
#ifndef DATA_H
#define DATA_H
class Data
{
private:int mA;
public:int getA(void);void setA(int a);
};
#endif
data.cpp
#include "data.h"
int Data::getA()
{return mA;
}
void Data::setA(int a)
{mA = a;
}
main.cpp
#include <iostream>
#include "data.h"
using namespace std;
int main(int argc, char *argv[])
{Data ob;ob.setA(100);cout<<ob.getA()<<endl;return 0;
}
对象的构造和析构:
当我们创建对象的时候,这个对象应该有一个初始状态,当对象销毁之前应该销毁自己创建的一些数据。对象的初始化和清理也是两个非常重要的安全问题,C++为了给我们提供这种问题的解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用,完成对象初始化(创建对象时为对象成员属性赋值)和对象清理工作。初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事。
四、构造函数(初始化工作)
1、概述
构造函数是类实例化对象时自动调用
2、创建
构造函数名与类名称相同,不能有返回值类型(连void都不可以),可以有参数(支持重载),必须加public权限。
3、分类
无参构造、有参构造。
4、调用
类实例化对象时:先为对象开辟空间 然后才调用构造函数。
1、如果用户不提供构造函数 编译器会自动 提供一个无参空的构造函数。
2、如果用户提供构造函数 编译器会自动 屏蔽默认无参的构造
四种:隐式调用、显示调用、隐式转换、匿名调用
注:写任何一个类 无参构造, 有参构造都需要实现
5、explicit关键字防止构造函数隐式转换
explicit修饰构造函数,防止构造函数隐式转换,避免令人产生赋值误会。
允许隐式转换:
//此时允许有参构造隐式转换
A(int a)
{mA = a;cout<<"A的有参构造mA="<<mA<<endl;
}
//构造函数隐式转换(类中只有一个数据成员)
A ob1=100;//ok
不允许隐式转换:
//防止有参构造 隐式转换
explicit A(int a)
{mA = a;cout<<"A的有参构造mA="<<mA<<endl;
}
//构造函数隐式转换(类中只有一个数据成员)
A ob1=100;//err 转换失败
五、析构函数(清理工作)
当对象生命周期结束的时候 系统自动调用析构函数。
函数名和类名称相同,在函数名前加~,没有返回值类型,没有函数形参。(不能被重载) 先调用析构函数 再释放对象的空间。
调用释放顺序:括号内的最先释放,释放先进后出
一般情况下,空的析构函数就足够。但是如果一个类有指针成员,这个类必须 写析构函数,释放指针成员所指向空间。
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
class Data
{
public:char *name;
public:Data(){name = NULL;}Data(char *str){name = (char*)calloc(1, strlen(str) + 1);strcpy(name, str);cout << "有参构造 name=" << name << endl;}~Data(){cout << "析构函数" <<name<< endl;if (name != NULL){free(name);name = NULL;}}};
void test()
{ Data ob("hello world");
}
六、拷贝构造函数
1、定义
拷贝构造函数本质是构造函数
拷贝构造的调用时机:旧对象 初始化 新对象 时。
如果用户不提供拷贝构造 编译器会自动提供一个默认的拷贝构造(完成赋值动作--浅拷贝)
2、拷贝构造 和 无参构造 有参构造的关系
如果用户定义了 拷贝构造或者有参构造 都会屏蔽无参构造。
如果用户定义了 无参构造或者有参构造 不会屏蔽拷贝构造。
3、拷贝构造调用形式
(1)旧对象给新对象初始化 调用拷贝构造
Data ob1(10);
Data ob2 = ob1;//调用拷贝构造
(2)普通对象作为函数参数 调用函数时 会发生拷贝构造
(3)函数返回值普通对象 (Visual Studio会发生拷贝构造)
Data get(void)
{Data ob1(10);return ob1;
}
void test()
{ Data ob2 = get();
}
注:给对象取别名 不会调用拷贝构造
Data ob1(10);
Data &ob2 = ob1;//不会调用拷贝构造
4、拷贝构造中浅拷贝和深拷贝
如果类中没有指针成员, 不用实现拷贝构造和析构函数。
如果类中有指针成员和拷贝构造调用, 必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作(因拷贝构造释放时相应堆区空间需再释放一次,故堆区空间也需重新拷贝一次)
七、初始化列表
成员对象:一个类的对象 作为另一个类的成员
类会自动调用成员对象的无参构造,如果类中想调用成员对象的有参构造 必须使用初始化列表。
class A
{
public:int mA;
public:A(){mA = 0;cout << "A的无参构造" << endl;}A(int a){mA = a;cout << "A的有参构造" << endl;}~A(){cout << "A的析构函数" << endl;}
};
class B
{
public:int mB;A ob;//成员对象
public:B(){cout << "B类的无参构造" << endl;}//初始化列表 成员对象 必须使用对象名+() 重要B(int a, int b) :ob(a){mB = b;cout << "B类的有参构造" << endl;}~B(){cout << "B的析构函数" << endl;}};
int main(int argc, char* argv[])
{B ob1(10, 20);cout << "mA =" << ob1.ob.mA << ", mB =" << ob1.mB << endl;return 0;}
八、对象数组
对象数组:本质是数组 数组的每个元素是对象