C++ Primer第七章②

C++ Primer

第七章 类

构造函数

每个类都分别定义了其对象初始化的方式,就是通过构造函数来控制其对象的初始化过程;构造函数的任务是:初始化对象的数据成员,只要类的对象被创建,就会执行构造函数。

构造函数非常复杂,这一节我们只介绍些基础的。
构造函数的名字和类名相同且没有返回类型,可以重载。
当我们创建类的一个const对象时,直到构造函数完成初始化过程对象才算是常量,所以构造函数在const对象的构造过程中可以向其写值。

你可以看到,上面我用了很多粗体,因为真的都是重要的金玉良言。

读到这不知道你有没有疑问,我们前面的那个Sales_data类并没有搞这个什么构造函数,不是说也可以完成项目拿到2000块吗?比如我们写过这样的代码:

Sales_data current;

那这个current是怎么初始化的呢?
这是我们编译器的功劳:如果我们的类没有显式地定义构造函数,编译器就会为我们隐式地定义一个默认构造函数,是不是很6,那这个默认构造函数是怎么初始化数据成员呢?

  • 如果存在类内的初始值,用它来初始化成员
    unsigned units_sold = 0; //售出册数
    double revenue = 0; //总销售收入
    
  • 否则,默认初始化该成员:bookNo为空字符串

最好是我们不用编译器默认生成构造函数,原因如下:

  • 因为我们自己才知道我们想把数据成员初始化成什么,
  • 有些成员比如指针默认初始化,值未定义会出问题,
  • 还有就是类包含类,被包含的那个没有类没有默认构造函数,那就报错了
  • 编译器只有在我们没有定义构造函数时,才会生成默认构造函数,也就是说,如果我们定义了一个构造函数,且它不是默认构造函数,那我们就没有默认构造函数可以使用了。

最保险就是自己亲力亲为。
当然,非常简单的类确实可以偷懒用编译器默认初始化
总之,记住一条,所有的行为都要在你掌控之中,即便是编译器生成默认构造函数,你也知道它具体干了什么。

我们来改造一下Sales_data类,给它搞几个构造函数:

struct Sales_data
{
    Sales_data() = default;

    Sales_data(const string &s) : bookNo(s) {}

    Sales_data(const string &s, unsigned n, double p) :
            (bookNo(s), units_sold(n), revenue(p*n)) {};

    Sales_data(istream &);
}

是不是很难懂啊,因为还有些知识没介绍,一个个来

= default

不接受任何实参的构造函数是默认构造函数,所以第一个是默认构造函数,= default的意思是,我们要求编译器生成构造函数。= default在类内部是内联,在类外部不是内联。

Sales_data(const string &s) : bookNo(s) {}

开始的s是传入参数,s用来初始化成员变量bookNo,这就是个简便写法,看得懂就行,等价于:

Sales_data(const string &s)
{
     bookNo = s;
}
在类的外部定义构造函数

我们来在类外面实现一下最后一个构造函数:

Sales_data::Sales_data(istream &is)
{
    read(is, *this); //调用原来的read函数从is中读取一条交易信息存入this所指的对象中
}

我们除了要初始化对象之外,还要控制拷贝、赋值和销毁对象时发生的行为,怎么样,再一次体会到了C++赋予程序员的自由以及责任吧,不过别紧张,现在暂时还不学,暂时都用编译器合成的。

访问控制与封装

这个太简单,我就不啰嗦了

class Sales_data
{
private:
    //数据成员
    string bookNo; //书号
    unsigned units_sold = 0; //售出册数
    double revenue = 0; //总销售收入

public:    
    //成员函数
    string isbn() const //返回书本isbn号,这里的const待会解释
    {
        return bookNo;
    }
    Sales_data& combine(const Sales_data&); //函数声明
    double avg_price() const; //返回售出书籍的均价,这里的const也待会解释
}

private后面的成员只能被类的成员函数访问,不能被使用该类的代码访问

graph LR
struct-->默认定义在第一个访问说明符之前的是public
graph LR
Aclass-->默认定义在第一个访问说明符之前的是privat

上面的Aclass就是class,因为格式问题 ,struct和class其他没有任何差别


那么问题来了,Sales_data类的数据成员都是private的,那我们的类外面的函数read等函数就无法编译了,因为它们无法访问成员变量。
解决方法就是让函数跟类做朋友

struct Sales_data
{
    friend istream &read(istream&, Sales_data&);

private:
    //数据成员
    string bookNo; //书号
    unsigned units_sold = 0; //售出册数
    double revenue = 0; //总销售收入

public:
    //成员函数
    string isbn() const //返回书本isbn号,这里的const待会解释
    {
        return bookNo;
    }
    Sales_data& combine(const Sales_data&); //函数声明
    double avg_price() const; //返回售出书籍的均价,这里的const也待会解释
}
istream &read(istream&, Sales_data&) //声明在类外且无作用域符号,为非成员函数
{
    double price = 0;
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

这里read函数的位置很暧昧,首先它是在类里面声明的,并且C++规定友元只能在类内部声明,但是它不能被public等访问控制符修饰,因为它不是类的成员函数,这样之后,我们在类外面声明定义它。

注意我这句话啊,在外面声明定义它,我们不是已经在类内部声明过了吗,外面只是定义而已啊,你是不是写错了?不是的,我故意这么写的,因为,友元的声明不同于一般的函数声明,它只是用来指定访问权限,其实没有函数声明的作用,所以,我们在类外部是要声明定义它的(当然,这个说法也不是很正确,不过我就这么理解了

#C++工程师#
全部评论

相关推荐

8 1 评论
分享
牛客网
牛客企业服务