C++笔记 -- 类(上)

本文围绕C++类展开,介绍了类的数据抽象和封装特性,包括接口与实现分离、隐藏实现细节等。还阐述了类的构造函数、析构函数,以及this指针、const成员函数。此外,讲解了访问控制、封装和友元的概念及作用,强调了封装的益处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类的基本思想是数据抽象和封装。数据抽象是一种依赖于接口和实现分离的编程技术。类的接口包括用户所能执行的操作;类的实现则包括类的数据成员,负责接口实现的函数体以及定义所需的各种私有函数。封装实现了类的接口和实现的分离。封装后的类能隐藏它的实现细节,类的用户只能使用接口而无法访问实现的具体部分。

类允许我们自己给应用定义新的类型,比如某个物体的属性。它的出现使得修改程序更容易,比如游戏角色的属性。

类有两个基本属性:一个是数据抽象(定义数据成员和函数成员);另一个是封装,保护类的成员不被随意访问的能力(private, protected)。

类还可以将其他类或函数设为友元,这样它们就能访问类的非公有成员。

类有默认构造函数和析构函数,构造函数是用来初始化对象,它是一种特殊的成员函数;析构函数是类结束后回收对象,也是一种特殊的成员函数。

比如我定义一个book类,书有名字,书号,编号等:

class Book {
    public:
        Book(string& book_name, string& book_no, int book_id, double revenue, double units_sold) : 
            bookName(book_name), bookNo(book_no), bookID(book_id), revenue(revenue), units_sold(units_sold) { }
        string isbn() const { return bookNo; }
        double book_price(string& book_name, int book_id);
        double avg_price() const;

    private:
        string bookName;
        string bookNo;
        int bookID;
        double revenue;
        double units_sold;
};

C++ Primer中的改进Sales_data类:

struct Sales_data {
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    double avg_price() const;
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

Sales_data add(const Sales_data&, const Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);

关于this

当我们调用成员函数时,实际上是在替某个对象调用它。成员函数通过一个名为this的额外的隐藏参数来访问调用它的那个对象。当我们调用一个成员函数时,用请求该函数的对象地址初始化this。this的目的是一直指向原来那个对象(我们自己调用的),所有this是一个常量指针,我们不能改变this中保存的地址。

引入const成员函数

默认情况下,this的类型是指向类类型非常量版本的常量指针。参数列表后面的const表示this是一个指向常量的指针。如:

std::string Sales_data::isbn(const Sales_data *const this)
{
    return this->isbn; 
}

 

在类的外部定义成员函数(对book类)

double Book::avg_price() const {
    return (this->revenue) / (this->units_sold);
}

 

定义构造函数(针对Sales_data类)

struct Sales_data {
    Sales_data() = default;
    Sales_data(const std::string &s) : bookNo(s) { }
    Sales_data(const std::string &s, unsigned n, double p) :
        bookNo(s), units_sold(n), revenue(p * n) { }
    Sales_data(std::istream &);
    std::string isbn() const { return bookNo; }
    Sales_data& combine(const Sales_data&);
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

 

访问控制与封装(我们重写Sales_data类,跟Book类相似)

class Sales_data {
    public:
        Sales_data() = default;
        Sales_data(const std::string &s, unsigned n, double p) :
            bookNo(s), units_sold(n), revenue(p * n) { }
        Sales_data(const std::string &s) : bookNo(s) { }
        Sales_data(std::istream&);
        std::string isbn() const { return bookNo; }
        Sales_data &combine(const Sales_data&);

    private:
        double avg_price() const {
            return units_sold ? revenue / units_sold : 0;
        }
        std::string bookNo;
        unsigned units_sold = 0;
        double revenue = 0.0;
};

一个类可以包含0个或多个访问说明符,而且对于某个访问说明符能出现多少次没有严格限定。每个访问说明符指定了接下来的成员的访问级别,其有效范围直到出现下一个访问说明符或直达类的结尾处为止。

 

友元

类允许其他类或函数访问它的非公有成员,方法是令其他类或函数成为它的友元。

如果类想把一个函数作为它的友元,只需要增加一条以friend关键字开始的函数声明即可。

友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限。

友元不是类的成员也不受它所在区域访问控制级别的约束。

但是,一般来说,最好在类定义开始或结束前的位置集中声明友元

友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明。

示例代码如下:

class Sales_data {
    friend Sales_data add(const Sales_data&, const Sales_data&);
    friend std::istream &read(std::istream&, Sales_data&);
    friend std::ostream &print(std::ostream&, const Sales_data&);

    public:
        Sales_data() = default;
        Sales_data(const std::string &s, unsigned n, double p) :
            bookNo(s), units_sold(n), revenue(p * n) { }
        Sales_data(const std::string &s) : bookNo(s) { }
        Sales_data(std::istream&);
        std::string isbn() const { return bookNo; }
        Sales_data &combine(const Sales_data&);

    private:
        std::string bookNo;
        unsigned units_sold = 0;
        double revenue = 0.0;
};

Sales_data add(const Sales_data&, const Sales_data&);
std::istream &read(std::istream&, Sales_data&);
std::ostream &print(std::ostream&, const Sales_data&);

 

封装的益处:

  1. 确保用户代码不会无意间破坏封装对象的状态。
  2. 被封装的类的具体实现细节可以随时改变,而无须调整用户级的代码。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值