第一章:类与对象

目录:

抽象

        对同一类对象的共同属性和腥味进行概括,形成

  •  先注意问题的本质,其次是实现过程或细节
  •  数据抽象:描述某类对象的属性或状态(对象相互区别的物理量)
  • 代码抽象:描述某类对象的共同行为特征或具有的功能
  • 抽象的实现:类
class Clock {
public:    // 1
    void setTime(int newH, int newM, int newS);    // 2
    void showTime();    // 2

private:    // 1
    int hour, minute, second;
};

1):特定的访问权限

2):外部接口

封装

        将抽象出的数据、代码封装在一起,形成类

  • 目的:增强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,  以特定的访问权限来使用类的成员
  • 实现封装:类声明中的 {}

类定义的语法形式

class 类名称 {
public:
    公有成员(外部接口)
private:
    私有成员
protected:
    保护成员
};

类成员的访问控制

        公有类型成员

        在关键字 public 后面声明,它们是类与外部的接口

        任何外部函数都可以访问公有类型数据和函数

        私有类型成员

        在关键字 private 后面声明,只允许本类中的函数访问

        类外部的任何函数都不能访问

        默认不加关键字即 private

        保护类型成员

        与 private 类似,其差别表现在继承与派生时对派生类的影响不同

类的成员函数

  • 在类中说明函数原型
  • 可以在类外给出函数体实现,并在函数名前使用雷鸣加以限定
  • 可以在类中给出函数体,形成内联成员函数
  • 允许声明重载函数和带默认参数值的函数

内联成员函数

提高效率,简单的函数可以在类中实现函数体,用 inline 关键字声明为内联函数

不能有复杂结构(循环,switch)

构造函数

  • 在对象被创建时使用特定的值构造对象
  • 函数名与类名相同
  • 不能定义返回值类型,也不能有 return 语句
  • 参数可有可无
  • 可以是内联函数
  • 可以重载
  • 可以带默认参数值

        对象被创建时自动调用 

默认构造函数

  • 参数表为空的构造函数
  • 全部参数都有默认值的构造函数

示例:

Clock();

Clock(int newH=0, int newM=0, int newS=0);

隐含生成的构造函数

如果程序中未定义构造函数,编译器将在需要时自动生成一个默认构造函数

  • 参数列表为空,不为数据成员设置初始值
  • 如果类内定义了成员的初始值,使用之;否则以默认方式初始化
  • 基本数据类型的数据默认初始化的值是不确定的

如果类中已定义构造函数,默认情况下编译器不在隐含生成默认构造函数

当然,也可以显示要求编译器生成

Clock() = default;    // 指示编译器提供默认构造函数

构造函数示例:

class Clock {
public:
    Clock(int newH, int newM, int newS);    // 构造函数
    Clock();    // 默认构造函数
private:
    int hour, minute, second;
};



// 构造函数的实现
Clock::Clock(int newH, int newM, int newS):hour(newH), minute(newM), second(newS) {}

// 默认构造函数的实现
Clock::Clock():hour(0),minute(0),second(0) {}



// 自动调用构造函数
Clock c1(0,0,0);    // 调用有参数的构造函数

Clock c2;    // 调用无参数的构造函数

委托构造函数

        类中往往有多个构造函数,只是参数表和初始化列表不同,其初始化算法都是相同的,这时为了避免代码重复,可以使用委托构造函数

        参考上述示例,默认构造函数可以修改为

Clock::Clock():Clock(0,0,0) {}     // 直接使用带参数的构造函数来执行初始化过程

复制构造函数

  • 用已经存在的对象去初始化新的对象
  • 隐含生成的复制构造函数可以实现对应数据成员一一复制
  • 自定义的复制构造函数可以实现特殊的复制功能
class 类名 {
public:
    类名(形参);    // 构造函数
    类名(const 类名 &对象名);    // 复制构造函数
};

类名:类(const 类名 &对象名)    // 复制构造函数的实现
{ 函数体}

三种被调用的情况:

  • 定义一个对象时,以本类另一个对象作为初始值
  • 函数的形参是类的对象,实参对象初始化形参对象时
  • 函数返回值是类的对象,返回时初始化一个临时无名对象

    示例:
    // 形参为类的对象
    void fun1(Point p) {}
    
    // 返回值为类的对象
    Point fun2() {
        Point a(1,2);
        return a;
    }
    
    int main() {
        Point a(4,5);
        Point b(a);    // 用本类对象初始化新的对象
    }

移动构造函数

 基于右值引用,通过移动数据方式构造新对象,与复制构造函数类似

参数为类对象的右值引用

不分配新内存,为配合异常捕获机制,需声明 noexcept 表示不会抛出异常

被移动的对象不应再使用,需要销毁或重新赋值

示例:

#include <utility>

class astring {
public:
    std::string s;
    astring (astring&& o) noexcept: s(std::move(o.s))    // 显示移动所有成员
    { 函数体 }
};

左右值引用:

  • 对持久存在变量的引用称为左值引用,& 表示
  • 对短暂存在可被移动的右值的引用称为右值引用,&& 表示
    float n = 6;
    float &lr_n = n;    // 左值引用
    float &&rr_n = n;    // 错误,右值引用不能绑定到左值
    float &&rr_n = n * n;    // 右值表达式绑定到右值引用
  • 通过标准库<utility> 中的 move 函数可将左值对象移动为右值
    float n = 10;
    float &&rr_n = std::move(n);    // 将 n 转化为右值,
                                    // move 函数承诺除对 n 重新赋值或销毁外,不以 rr_n 以外方式使用

析构函数 

  • 完成对象被删除前的一些清理工作
  • 在对象的生存期结束的时候系统自动调用,然后再释放此对象所属的空间
  • 如果未声明,编译器自动产生一个默认的析构函数,函数体为空
class Point {
public:
    ~Point();    // 析构函数
};

Point::~Point() {}

组合

  • 类中的成员是另一个类的对象
  • 可以在已有抽象的基础上实现更复杂的抽象

    构造组合类对象时的初始化次序
  • 首先对构造函数初始化列表中列出的成员(基础类型成员,对象成员)初始化,初始化次序是成员在类体中定义的次序
  • 成员对象构造函数调用顺序:按对象成员的声明顺序,先声明者先构造
  • 初始化列表中未出现的成员对象,调用默认构造函数初始化
  • 处理完初始化列表后,再执行构造函数的函数体
// Point 类
class Point {
public:
    Point(int xx=0, int yy=0) {
        x = xx;
        y = yy;
    }
    Point(Point &p);

private:
    int x, y;
};
Point::Point(Point &p) {
    x = p.x;
    y = p.y;
}



// Line 类
class Line {
public:
    Line(Point xp1, Point xp2);
    Line(Line &l);

private:
    Point p1, p2;    // Point 类的对象
    double len;
};
// 组合类的构造函数
Line::Line(Point xp1, Point xp2) : p1(xp1), p2(xp2) {
    函数体
}

// 组合类的复制构造函数
Line::Line(Line &l) : p1(l.p1), p2(l.p2) {
    函数体
}



int main() {
    Point myp1(1,1), myp2(4,5);    // 建立 Point 类的对象
    Line line(myp1, myp2);    // 建立 Line 类的对象
    Line line2(line);    // 利用复制构造函数建立一个新对象
}

前项引用声明

两个类互为引用编译会报错,解决编译问题

class B;    // 前项引用声明

class A {
public:
    void f(B b);
};

class B {
public:
    void g(A a);
};
  • 在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象
  • 当使用前项引用声明时,只能使用被声明的符号,而不能涉及类的任何细节
class Fred;    // 前项引用声明

class Barney {
    Fred x;    // 错误
};

class Fred {
    Barney y;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值