C++基础之类和对象

*2**. 类和对象*

*2**.1 类和对象的基本概念*

*2**.1.1 C和C++中struct区别*

n c语言struct只有变量

n c++语言struct 既有变量,也有函数

*2**.1.2 类的封装*

我们编写程序的目的是为了解决现实中的问题,而这些问题的构成都是由各种事物组成,我们在计算机中要解决这种问题,首先要做就是要将这个问题的参与者:事和物抽象到计算机程序中,也就是用程序语言表示现实的事物。

那么现在问题是如何用程序语言来表示现实事物?现实世界的事物所具有的共性就是每个事物都具有自身的属性,一些自身具有的行为,所以如果我们能把事物的属性和行为表示出来,那么就可以抽象出来这个事物。

比如我们要表示人这个对象,在c语言中,我们可以这么表示:

typedef struct _Person{
    char name[64];
    int age;
}Person;
typedef struct _Aninal{
    char name[64];
    int age;
    int type; //动物种类
}Ainmal;
​
void PersonEat(Person* person){
    printf("%s在吃人吃的饭!\n",person->name);
}
void AnimalEat(Ainmal* animal){
    printf("%s在吃动物吃的饭!\n", animal->name);
}
​
int main(){
​
    Person person;
    strcpy(person.name, "小明");
    person.age = 30;
    AnimalEat(&person);
​
    return EXIT_SUCCESS;
}

定义一个结构体用来表示一个对象所包含的属性,函数用来表示一个对象所具有的行为,这样我们就表示出来一个事物,在c语言中,行为和属性是分开的,也就是说吃饭这个属性不属于某类对象,而属于所有的共同的数据,所以不单单是PeopleEat可以调用Person数据,AnimalEat也可以调用Person数据,那么万一调用错误,将会导致问题发生。

从这个案例我们应该可以体会到,属性和行为应该放在一起,一起表示一个具有属性和行为的对象。

假如某对象的某项属性不想被外界获知,比如说漂亮女孩的年龄不想被其他人知道,那么年龄这条属性应该作为女孩自己知道的属性;或者女孩的某些行为不想让外界知道,只需要自己知道就可以。那么这种情况下,封装应该再提供一种机制能够给属性和行为的访问权限控制住。

所以说封装特性包含两个方面,一个是属性和变量合成一个整体,一个是给属性和函数增加访问权限。

n 封装

\1. 把变量(属性)和函数(操作)合成一个整体,封装在一个类中

\2. 对变量和函数进行访问控制

n 访问权限

\1. 在类的内部(作用域范围内),没有访问权限之分,所有成员可以相互访问

\2. 在类的外部(作用域范围外),访问权限才有意义:public,private,protected

\3. 在类的外部,只有public修饰的成员才能被访问,在没有涉及继承与派生时, private和protected是同等级的,外部不允许访问

img

//封装两层含义
//1. 属性和行为合成一个整体
//2. 访问控制,现实事物本身有些属性和行为是不对外开放
class Person{
//人具有的行为(函数)
public:
    void Dese(){ cout << "我有钱,年轻,个子又高,就爱嘚瑟!" << endl;}
//人的属性(变量)
public:
    int mTall; //多高,可以让外人知道
protected:
    int mMoney; // 有多少钱,只能儿子孙子知道
private:
    int mAge; //年龄,不想让外人知道
};
​
int main(){
​
    Person p;
    p.mTall = 220;
    //p.mMoney 保护成员外部无法访问
    //p.mAge 私有成员外部无法访问
    p.Dese();
​
    return EXIT_SUCCESS;
}

*2**.1.3 将成员变量设置为private*

\1. 可赋予客户端访问数据的一致性。

如果成员变量不是public,客户端唯一能够访问对象的方法就是通过成员函数。如果类中所有public权限的成员都是函数,客户在访问类成员时只会默认访问函数,不需要考虑访问的成员需不需要添加(),这就省下了许多搔首弄耳的时间。

\2. 可细微划分访问控制。

使用成员函数可使得我们对变量的控制处理更加精细。如果我们让所有的成员变量为public,每个人都可以读写它。如果我们设置为private,我们可以实现“不准访问”、“只读访问”、“读写访问”,甚至你可以写出“只写访问”。

*2**.1.3练习*

请设计一个Maker类,Maker类具有name和age属性,提供初始化函数(Init),并提供对name和age的读写函数(set,get),但必须确保age的赋值在有效范围内(0-100),超出有效范围,则拒绝赋值,并提供方法输出姓名和年龄.(10分钟)

*2**.2 面向对象程序设计案例*

*2**.2.1 设计立方体类*

设计立方体类(Cube),求出立方体的面积( 2ab + 2ac + 2bc )和体积( a * b * c),分别用*全局函数**成员函数*判断两个立方体是否相等。

img

//立方体类
class Cub{
public:
    void setL(int l){ mL = l; }
    void setW(int w){ mW = w; }
    void setH(int h){ mH = h; }
    int getL(){ return mL; }
    int getW(){ return mW; }
    int getH(){ return mH; }
    //立方体面积
    int caculateS(){ return (mL*mW + mL*mH + mW*mH) * 2; }
    //立方体体积
    int caculateV(){ return mL * mW * mH; }
    //成员方法
    bool CubCompare(Cub& c){
        if (getL() == c.getL() && getW() == c.getW() && getH() == c.getH()){
            return true;
        }
        return false;
    }
private:
    int mL; //长
    int mW; //宽
    int mH; //高
};
​
//比较两个立方体是否相等
bool CubCompare(Cub& c1, Cub& c2){
    if (c1.getL() == c2.getL() && c1.getW() == c2.getW() && c1.getH() == c2.getH()){
        return true;
    }
    return false;
}
​
void test(){
    Cub c1, c2;
    c1.setL(10);
    c1.setW(20);
    c1.setH(30);
​
    c2.setL(20);
    c2.setW(20);
    c2.setH(30);
​
    cout << "c1面积:" << c1.caculateS() << " 体积:" << c1.caculateV() << endl;
    cout << "c2面积:" << c2.caculateS() << " 体积:" << c2.caculateV() << endl;
​
    //比较两个立方体是否相等
    if (CubCompare(c1, c2)){
        cout << "c1和c2相等!" << endl;
    }
    else{
        cout << "c1和c2不相等!" << endl;
    }
​
    if (c1.CubCompare(c2)){
        cout << "c1和c2相等!" << endl;
    }
    else{
        cout << "c1和c2不相等!" << endl;
    }
}

*2**.2.2 点和圆的关系*

设计一个圆形类(AdvCircle),和一个点类(Point),计算点和圆的关系。

假如圆心坐标为x0, y0, 半径为r,点的坐标为x1, y1:

1)点在圆上:(x1-x0)(x1-x0) + (y1-y0)(y1-y0) == r*r

2)点在圆内:(x1-x0)(x1-x0) + (y1-y0)(y1-y0) < r*r

3)点在圆外:(x1-x0)(x1-x0) + (y1-y0)(y1-y0) > r*r

//点类
class Point{
public:
    void setX(int x){ mX = x; }
    void setY(int y){ mY = y; }
    int getX(){ return mX; }
    int getY(){ return mY; }
private:
    int mX;
    int mY;
};
​
//圆类
class Circle{
public:
    void setP(int x,int y){
        mP.setX(x);
        mP.setY(y);
    }
    void setR(int r){ mR = r; }
    Point& getP(){ return mP; }
    int getR(){ return mR; }
    //判断点和圆的关系
    void IsPointInCircle(Point& point){
        int distance = (point.getX() - mP.getX()) * (point.getX() - mP.getX()) + (point.getY() - mP.getY()) * (point.getY() - mP.getY());
        int radius = mR * mR;
        if (distance < radius){
            cout << "Point(" << point.getX() << "," << point.getY() << ")在圆内!" << endl;
        }
        else if (distance > radius){
            cout << "Point(" << point.getX() << "," << point.getY() << ")在圆外!" << endl;
        }
        else{
            cout << "Point(" << point.getX() << "," << point.getY() << ")在圆上!" << endl;
        }
    }
private:
    Point mP; //圆心
    int mR; //半径
};
​
void test(){
    //实例化圆对象
    Circle circle;
    circle.setP(20, 20);
    circle.setR(5);
    //实例化点对象
    Point point;
    point.setX(25);
    point.setY(20);
​
    circle.IsPointInCircle(point);
}

*2**.2.3 分文件实现*

对于第二个案例,类的声明和类的定义分别在.h和.cpp实现。

img

****代码:********示例代码\28 面向对象程序设计_案例2分文件编写****

*2**.3 对象的构造和析构*

*2**.3.1 初始化和清理*

我们大家在购买一台电脑或者手机,或者其他的产品,这些产品都有一个初始设置,也就是这些产品对被创建的时候会有一个基础属性值。那么随着我们使用手机和电脑的时间越来越久,那么电脑和手机会慢慢被我们手动创建很多文件数据,某一天我们不用手机或电脑了,那么我们应该将电脑或手机中我们增加的数据删除掉,保护自己的信息数据。

从这样的过程中,我们体会一下,所有的事物在起初的时候都应该有个初始状态,当这个事物完成其使命时,应该及时清除外界作用于上面的一些信息数据。

那么我们c++中OO思想也是来源于现实,是对现实事物的抽象模拟,具体来说,当我们创建对象的时候,这个对象应该有一个初始状态,当对象销毁之前应该销毁自己创建的一些数据。

对象的初始化和清理也是两个非常重要的安全问题,一个对象或者变量没有初始时,对其使用后果是未知,同样的使用完一个变量,没有及时清理,也会造成一定的安全问题。c++为了给我们提供这种问题的解决方案,****构造函数**********析构函数******,这两个函数将会被编译器自动调用,完成对象初始化和对象清理工作。

****无论你是否喜欢,对象的初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事,所以编写类就应该顺便提供初始化函数。******

为什么初始化操作是自动调用而不是手动调用?既然是必须操作,那么自动调用会更好,如果靠程序员自觉,那么就会存在遗漏初始化的情况出现。

*2**.3.1 构造函数和析构函数*

构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

析构函数主要用于对象****销毁前******系统自动调用,执行一些清理工作。

****构造函数语法:******

n 构造函数函数名和类名相同,没有返回值,不能有void,但可以有参数。n ClassName(){}

****析构函数语法:******

n 析构函数函数名是在类名前面加”~”组成,没有返回值,不能有void,不能有参数,不能重载。n ~ClassName(){}

class Person*{*public*:* Person*(){* cout *<<* "构造函数调用!" *<<* endl*;* pName *=* *(*char*)**malloc*(**sizeof(*"John"**));* strcpy*(**pName*,* "John"*);* mTall *=* 150*;* mMoney *=* 100*;* *}* *~*Person*(){* cout *<<* "析构函数调用!" *<<* endl*;* *if* *(*pName *!=* *NULL**){** free***(*pName*);** pName **=** **NULL**;* **}* *}*public*:* char*** pName*;* int mTall*;* int mMoney*;**};** void test***(){** Person person**;** cout **<<** person**.**pName **<<** person**.**mTall **<<** person**.**mMoney **<<** endl**;**}**

*2**.3.1 构造函数的分类及调用*

n 按参数类型:分为无参构造函数和有参构造函数

n 按类型分类:普通构造函数和拷贝构造函数(复制构造函数)

class Person*{*public*:* Person*(){* cout *<<* "no param constructor!" *<<* endl*;* mAge *=* 0*;* *}* //有参构造函数 Person*(*int age*){* cout *<<* "1 param constructor!" *<<* endl*;* mAge *=* age*;* *}* //拷贝构造函数(复制构造函数) 使用另一个对象初始化本对象 Person*(*const Person*&* person*){* cout *<<* "copy constructor!" *<<* endl*;* mAge *=* person*.*mAge*;* *}* //打印年龄 void PrintPerson*(){* cout *<<* "Age:" *<<* mAge *<<* endl*;* *}*private*:* int mAge*;**};*//1. 无参构造调用方式void test01**(){ * //调用无参构造函数 Person person1*;** person1*.*PrintPerson*();* //无参构造函数错误调用方式 //Person person2(); //person2.PrintPerson();*}*//2. 调用有参构造函数void test02*(){* //第一种 括号法,最常用 Person person01*(*100*);* person01*.*PrintPerson*();* //调用拷贝构造函数 Person person02*(*person01*);* person02*.*PrintPerson*();* //第二种 匿名对象(显示调用构造函数) Person*(*200*);* //匿名对象,没有名字的对象 Person person03 *=* Person*(*300*);* person03*.*PrintPerson*();* //注意: 使用匿名对象初始化判断调用哪一个构造函数,要看匿名对象的参数类型 Person person06*(*Person*(*200*));* //等价于 Person person06 = Person(200); person06*.*PrintPerson*();* //第三种 =号法 隐式转换 Person person02 *=* 100*;* //Person person02 = Person(100) person02*.*PrintPerson*();* //调用拷贝构造 Person person05 *=* person02*;* //Person person05 = Person(person02) person05*.*PrintPerson*();***}**

****b为A的实例化对象,A a = A(b) 和 A(b)的区别?****** 当A(b) 有变量来接的时候,那么编译器认为他是一个匿名对象,当没有变量来接的时候,编译器认为你A(b) 等价于 A b.

****注意:******不能调用拷贝构造函数去初始化匿名对象,也就是说以下代码不正确:

class Teacher*{*public*:* Teacher*(){* cout *<<* "默认构造函数!" *<<* endl*;* *}* Teacher*(*const Teacher*&* teacher*){* cout *<<* "拷贝构造函数!" *<<* endl*;* *}*public*:* int mAge*;**};*void test**(){ * Teacher t1*;** //error C2086:“Teacher t1”: 重定义 Teacher*(*t1*);* //此时等价于 Teacher t1;*}*

*2**.3.2 拷贝构造函数的调用时机*

n 对象以值传递的方式传给函数参数

n 函数局部对象以值传递的方式从函数返回(vs debug模式下调用一次拷贝构造,qt不调用任何构造)

n 用一个对象初始化另一个对象

class Person*{*public*:* Person*(){* cout *<<* "no param contructor!" *<<* endl*;* mAge *=* 10*;* *}* Person*(*int age*){* cout *<<* "param constructor!" *<<* endl*;* mAge *=* age*;* *}* Person*(*const Person*&* person*){* cout *<<* "copy constructor!" *<<* endl*;* mAge *=* person*.*mAge*;* *}* *~*Person*(){* cout *<<* "destructor!" *<<* endl*;* *}*public*:* int mAge*;**};**//1. 旧对象初始化新对象void test01***(){** Person p**(**10**);**** Person p1****(**p**);**** Person p2 ***

  • 38
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

左倾红黑树交换中序后缀节点左旋平衡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值