C++类与对象

C++ Primer Plus读书笔记

类与对象

1.面向对象编程和过程性编程

  1. 编程模型
    所有计算机均由两种元素组成:代码和数据.精确的说,有些程序是围绕着”什么正在发生”而编写,有些则是围绕”谁正在受影响”而编写的.
    • 第一种编程方式叫做”面向过程的模型”,按这种模型编写的程序以一系列的线性步骤(代码)为特征,可被理解为作用于数据的代码.如 C 等过程化语言.
    • 第二种编程方式叫做”面向对象的模型”,按这种模型编写的程序围绕着程序的数据(对象)和针对该对象而严格定义的接口来组织程序,它的特点是数据控制代码的访问.通过把控制权转移到数据上,面向对象的模型在组织方式上有:抽象,封装,继承和多态的好处.
  2. 抽象

    • 面向对象程序设计的基本要素是抽象,可以根据物理含义分解一个复杂的系统,把它划分成更容易管理的块.

    • 例如,一个计算机系统是一个独立的对象.而在计算机系统内部由几个子系统组成:显示器,键盘,硬盘驱动器,DVD-ROM,软盘,音响等,这些子系统每个又由专门的部件组成.关键是需要使用层次抽象来管理计算机系统(或其他任何复杂系统)的复杂性.

  3. 封装
    封装是一种把代码和代码所操作的数据捆绑在一起,使这两者不受外界干扰和误用的机制.

    • 封装可被理解为一种用做保护的包装器,以防止代码和数据被包装器外部所定义的其他代码任意访问.对包装器内部代码与数据的访问通过一个明确定义的接口来控制.

    • 封装代码的好处是每个人都知道怎样访问代码,进而无需考虑实现细节就能直接使用它,同时不用担心不可预料的副作用.

    • 一个类定义着将由一组对象所共享的行为(数据和代码).一个类的每个对象均包含它所定义的结构与行为,这些对象就好象是一个模子铸造出来的.所以对象也叫做类的实例.
    • 在定义一个类时,需要指定构成该类的代码与数据.特别是,类所定义的对象叫做成员变量或实例变量.操作数据的代码叫做成员方法.方法定义怎样使用成员变量,这意味着类的行为和接口要由操作实例数据的方法来定义.
    • 类的公有接口代表外部的用户应该知道或可以知道的每件东西.
    • 私有的方法数据只能通过该类的成员代码来访问.这就可以确保不会发生不希望的事情.
  4. 继承
    继承是指一个对象从另一个对象中获得属性的过程.是面向对象程序设计的三大原则之二,它支持按层次分类的概念.

    • 例如,波斯猫是猫的一种,猫又是哺乳动物的一种,哺乳动物又是动物的一种.如果不使用层次的概念,每个对象需要明确定义各自的全部特征.通过层次分类方式,一个对象只需要在它的类中定义是它成为唯一的各个属性,然后从父类中继承它的通用属性.

    • 因此,正是由于继承机制,才使得一个对象可以成为一个通用类的一个特定实例.一个深度继承的子类将继承它在类层次中的每个祖先的所有属性. 继承与封装可以互相作用.

    • 如果一个给定的类封装了某些属性,它的任何子类将会含有同样得属性,另加各个子类所有得属性.这是面向对象程序在复杂性上呈线性而非几何增长的一个重要概念.新的子类继承其所有祖先的所有属性.子类和系统中的其他代码不会产生无法预料的交互作用.
  5. 多态
    多态是指一个方法只能有一个名称,但可以有许多形态,也就是程序中可以定义多个同名的方法,用”一个接口,多个方法”来描述.可以通过方法的参数和类型引用.

  6. 使用类的好处在于能够将代码的控制交给数据,将数据与程序合为一体,管理起来更加整齐,而且相对于面向过程的编程来说更加简洁,同时其继承又使得编程变得更加方便。类就相当于结构的一个加强版,总的来说,结构也能实现继承、多态,能包含成员函数。 唯一的区别就是二者的默认数据访问控制类型不同。

2. 类

  • 类的访问控制:关键字private和public用来描述对类成员的访问控制,使用类对象的程序都可以直接访问public部分,但是只有成员函数友元函数才能访问private部分,将数据隐藏(使用private)也是数据封装的一种形式。

  • 类与结构(class与struct)的唯一区别在于,结构的默认访问类型是public,而类的默认访问类型则是private。

  • 类的内联方式:定义位于类的声明中的函数都自动成为内联函数

  • 使用类之前需要创建类对象,可以用new为对象分配存储空间,可以将对象作为函数的返回值和参数,相似的类的对象之间可以转换

  • 对于一个类来说,sizeof()的结果是类内所有元素的大小,类内的函数都不计算在内。

3.类的构造与析构

  • 使用构造与析构函数的原因:在初始化对象的时候由于某些数据是private不能够直接访问的,因此需要借助类中的函数进行初始化,因此就有了构造函数和析构函数这一说。

  • 构造函数是一个没有返回类型的函数。

  • 一般来说,要留一个空参数的默认构造函数,主要为了防止没有显式制定
//头文件
#ifndef ZHUMENG_H_
#define ZHUMENG_H

#include<iostream>

class ZhuMeng
{
private:
    float yanyiquan;
public:
    ZhuMeng(float a);
    ~ZhuMeng();
    void Show();
};

#endif
//实现文件
#include"ZhuMeng.h"

ZhuMeng::ZhuMeng(float a)
{
    yanyiquan = a;
}

ZhuMeng::~ZhuMeng()
{

}

void ZhuMeng::Show()
{
    std::cout << yanyiquan << std::endl;
}
//调用文件
#include<iostream>
#include"ZhuMeng.h"
using namespace std;


int main() 
{
    ZhuMeng quanquan(3.14);
    ZhuMeng chunjie(4.16);
    quanquan.Show();
    chunjie.Show();
    return 0;
}
  • 析构函数:如果在构造函数中使用new分配内存,则析构函数将使用delete来释放这些内存,但如果没有使用new分配内存的话,则不需要在析构函数中做这些事情。

4.this指针

  • 每个对象都拥有一个指针:this指针,通过this指针来访问自己的地址。

  • 注:this指针并不是对象的一部分,this指针所占的内存大小是不会反应在sizeof操作符上的。

  • this在成员函数的开始执行前构造的,在成员的执行结束后清除。
  • this 就是这个类(层级最近的)自己(做为指针而非引用),-> 就是调用类指针的某个成员变量或者函数。当你在类的实现里使用类的各种方法、成员变量的时候,如果不加上 this-> 编译器会自动的给你加上。有一些时候,你不得不显示的加上,因为函数参数名如果和成员变量重名,编译器会优先选层级近的,这时候如果要用成员变量,需要显式的加上 this-> 。
  • 等于说一般情况下this是没有什么作用的,只是在特定的情况下才必须使用this指针。

5.const

  • 函数的前面和后面加const效果是不太相同的,
    • 在函数前加const表示返回型为const型;
    • 在函数后面加const表示函数不能修改类内数据成员;
    • 在函数的参数前加const是另一种写法,表示这个内容在函数执行期间不会被更改。
//在类内声明为:
int func(int ) const;
//表示为const成员函数,在任何情况下该函数都不会修改数据成员。

6.运算符重载

  • 在类里面重载运算符,主要是为了使得运算符适应于这个类。

  • 注意:运算符重载的函数只能带一个参数,假设重载一个加法,this指针指向的是运算符左边的对象,同时,运算符右边的数据类型不限,但是需要在定义重载运算函数的参数列表中确定。

其实应该这样看,比如重载了一个运算符+,这时候
C=A+B 被解释为
C=A.operator+(B)
//头文件
#ifndef ZHUMENG_H_
#define ZHUMENG_H

#include<iostream>

class ZhuMeng
{
private:
    int shi;
    int fen;
    int miao;
public:
    ZhuMeng();
    ZhuMeng(int );
    ZhuMeng(int , int , int );
    ~ZhuMeng();
    ZhuMeng operator+(ZhuMeng &) const;
};

#endif
//实现文件
#include"ZhuMeng.h"

ZhuMeng::ZhuMeng()
{
    shi = 0;
    fen = 0;
    miao = 0;
}

ZhuMeng::ZhuMeng(int shit)
{
    this->shi = shit;
}

ZhuMeng::ZhuMeng(int shi, int fen, int miao)
{
    this->shi = shi;
    this->fen = fen;
    this->miao = miao;
}

operator double()
{
    return this->miao;
}

ZhuMeng::~ZhuMeng()
{

}

ZhuMeng ZhuMeng::operator+(ZhuMeng & aaa) const 
{
    std::cout << this->shi << std::endl;
    ZhuMeng sum;
    sum.shi = this->shi + aaa.shi;
    sum.fen = this->fen + aaa.fen;
    sum.miao = this->miao + aaa.miao;
    return sum;
}
//调用
#include<iostream>
#include"ZhuMeng.h"
using namespace std;


int main() 
{
    ZhuMeng default_zhumeng;
    ZhuMeng a1(13, 24, 10);
    ZhuMeng a2(3, 3, 3);
    default_zhumeng = a1 + a2;
    return 0;
}
  • 由于某些运算符的左右类型不同,比如一个类+一个int型,这样如果把int写在加号的左边就会出现问题

    • A+2.4
    • 2.4+A
    • 上面两种描述是不同的,这时候使用非成员函数进行运算符重载会比较合适
  • 非成员函数的运算符重载是不写在类内的,在类外进行定义的时候重载运算符的参数有两个。下面是一个实例:

ZhuMeng operator*(int all, ZhuMeng all_2)
{
    cout<<"success"<<endl;
}

int main() 
{
    ZhuMeng default_zhumeng;
    ZhuMeng a1(13, 24, 10);
    ZhuMeng a2(3, 3, 3);
    default_zhumeng = 3*a1;
    return 0;
}
  • 重载的限制:

    1. 以下运算符不可以重载

      • sizeof
      • . 成员运算符
      • .* 成员指针运算符
      • :: 作用域解析运算符
      • ?: 条件运算符
      • typeid RTTI运算符
      • const_cast 强制类型转换
      • dynamic_cast 强制类型转换
      • reinterpret_cast 强制类型转换
      • static_cast 强制类型转换
    2. 不能创建新的运算符,例如不能使用operator**()函数来创建运算符

7.友元

  • 定义:使用非成员函数可以按所需的顺序获得操作数,但是这样的话会引入一个新的问题,非成员函数不能直接访问private,这时候就引出了友元函数的概念,友元函数(非成员函数)是可以访问类的私有成员的

  • 友元有三大类

    • 友元函数
    • 友元类
    • 友元成员函数
  • 创建友元

    • 第一步:把其原型放在类声明中,并在原型声明前加上关键字friend。例如

      friend ZhuMeng operator+(int a, ZhuMeng b);
    • 上面的原型意味着

      1. 虽然operator+()函数是在类声明中声明的,但是它不是成员函数,不能使用成员运算符来调用。
      2. 虽然operator+()函数不是成员函数,但它与成员函数的访问权限相同。
    • 第二步:编写函数定义
      • 因为它不是成员函数,因此不需要使用ZhuMeng::限定符。另外,在定义的时候不需要带上friend关键字

8.类的自动转换和类型转换

  • 类的构造函数中定义了某种方法,则可以直接用构造函数的参数列表给一个对象逐成员赋值,例如下面两行代码就是。

  • 下面代码的第四行,也就类似于默认的强制类型转换,将一个整数类型强制转换成了类的对象。

ZhuMeng default_zhumeng;
int k = 3;
default_zhumeng = 3,3,3;//隐式强制类型转换
default_zhumeng = ZhuMeng(k);//显式强制类型转换
  • 如果在构造函数的声明中使用了关键字explicit,则只能通过显式进行强制类型转换而不能通过隐式强制类型转换。

  • 转换函数:将一个特定类的对象转换成默认的类型,转换函数是没有返回值类型的,也没有参数列表,这里转换函数的形式为:

operator typeName()
  • 如果定义了多个类型的不同转换函数,则不能使用隐式的自动转换,因为可能会产生二义性,编译器不知道使用哪一种转换函数,因此需要使用显式的转换方式进行转换。

  • 转换函数与友元函数

    • 比如要实现一个特定类的加法,有一种可行的策略是使用类型转换类进行,也可以设置成员函数或非成员函数进行操作,都是可行的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值