C++ Primer 学习笔记_51_类与数据抽象 --构造函数【上】

--构造函数【上】

引言:

构造函数确保每个对象在创建时自动调用,以确保每个对象的数据成员都有合适的初始值。

class Sales_item
{
public:
    //其中isbn由string的默认构造函数提供初始化
    Sales_item():units_sold(0),revenue(0){}

private:
    std::string isbn;
    unsigned units_sold;
    double revenue;
};

构造函数的几大特征:

1、构造函数可以被重载

一般而言,不同的构造函数允许用户指定不同的方式来初始化数据成员。

class Sales_item
{
    //other members as before
public:
    Sales_item();
    Sales_item(const string &);
    Sales_item(std::istream &);
};

2、实参决定使用哪个构造函数

上面我们定义了三个构造函数,在定义新对象时,可以使用这些构造函数中的任意一个:

    Sales_item empty;
    Sales_item Primer_3td_Ed("0-201-82470-1");
    Sales_item Primer_4th_Ed(cin);

3、构造函数自动执行

只要创建该类型的一个对象,编译器就自动运行一个构造函数:

    //调用含有一个string形参的构造函数
    Sales_item Primer_2cn_Ed("0-201-54848-8");
    //调用默认构造函数初始化该对象
    Sales_item *p = new Sales_item();

4、用于const对象的构造函数

class Sales_item
{
public:
    //构造函数不能是const
    Sales_item() const; //Error
};

创建类类型的const对象时,运行一个普通构造函数就可以初始化该const对象。构造函数的工作就是初始化对象,不管对象是否为const,都用一个构造函数来初始化对象(瞬间对构造函数有种敬仰之情...).

//P387 习题12.19
class NoName
{
public:
    NoName():pstring(0),ival(0),dval(0){}
    NoName(std::string *Pstr,int Ival,double Dval):pstring(Pstr),ival(Ival),dval(Dval){}
private:
    std::string *pstring;
    int ival;
    double dval;
};

一、构造函数初始化式

构造函数的初始化列表以一个冒号可是,接着是一个以逗号分割的数据成员列表,每个数据成员跟一个放在圆括号中的初始化式。

构造函数可以定义在类的内部或外部,但是构造函数的初始化式只在构造函数的定义中而不是在声明中指定。

【小心O(∩_∩)O~】

构造函数初始化列表是许多相当有经验的C++程序员都没有掌握的一个特性。

    Sales_item(const std::string &book)
    {
        //其实在执行这一条语句之前,isbn已经有值了
        //这个构造函数隐式使用string构造函数初始化isbn
        isbn = book;
        units_sold = 0;
        revenue = 0;
    }

从概念上将,可以认为构造函数分为两个阶段执行:

1)初始化阶段;

2)普通的计算阶段。计算阶段由构造函数函数体中的所有语句组成。

不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。初始化发生在计算阶段开始之前

在构造函数初始化列表中没有显式提及的每个成员,使用与初始化变量相同的规则来进行初始化。运行该类型的默认构造函数,来初始化类类型的数据成员。内置或复合类型的成员的初始值依赖于对象的作用域:在局部作用域中这些成员不被初始化,而在全局作用域中它们被初始化为0

使用构造函数初始化列表的版本初始化【重点是“初始化”】数据成员,没有定义初始化列表的构造函数版本在构造函数函数体中对数据成员赋值【亮点在”赋值“】。这个区别的重要性取决于数据成员的类型。

1、有时需要构造函数初始化列表

有些成员必须在构造函数初始化列表中进行初始化。对于这样的成员,在构造函数函数体中对它们赋值不起作用。没有默认构造函数的类类型的成员,以及const或引用类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。

class ConstRef
{
public:
    ConstRef(int ii);

private:
    int i;
    const int ci;
    int &ri;
};
ConstRef::ConstRef(int ii)
{
    i = ii;     //OK
    ci = ii;    //Error
    ri = ii;    //没有编译错误,但是ri事实上根本没有绑定任何对象
}

谨记:可以初始化const对象或引用类型的对象,初始化const或引用类型数据成员的唯一机会是构造函数初始化列表中。编写该构造函数的正确方式为:

class ConstRef
{
public:
    ConstRef(int ii);

private:
    int i;
    const int ci;
    int &ri;
};
ConstRef::ConstRef(int ii):i(ii),ci(ii),ri(ii){}

【建议:使用构造函数初始化列表,P389】

在许多类中,初始化和赋值严格来讲都是低效率的:数据成员可能已经被直接初始化了,还要对它进行初始化和赋值。比较率问题更重要的是,某些数据成员必须要初始化,这是一个事实

因此,必须对任何const或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式。

当类成员需要使用初始化列表时,通过常规地使用构造函数初始化列表,就可以避免发生编译时错误。

2、成员初始化的次序

每个成员在构造函数初始化列表中只能指定一次;

构造函数初始化列表仅指定用于初始化成员的值,并不指定这些初始化执行的次序。成员被初始化的次序就是定义成员的次序!

初始化的次序常常无关紧要。然而,如果一个成员是根据其他成员而初始化,则成员初始化的次序是至关重要的

class X
{
    int i;
    int j;

public:
    X(int val):j(val),i(j){}	//在GCC编译器上会给出警告
};

按照与成员声明一致的次序编写构造函数初始化列表是个好主意。此外,尽可能避免使用成员来初始化其他成员。因此,一般情况下,通过(重复)使用构造函数的形参而不是使用对象的数据成员!

    X(int val):j(val),i(val){}

3、初始化式可以是任意复杂的表达式

    Sales_item(const std::string &book,int cnt,double price):
        isbn(book),units_sold(cnt),revenue(cnt * price) {}

4、类类型的数据成员的初始化式

初始化类类型的成员时,要指定实参并传递给成员类型的一个构造函数。可以使用该类型的任意构造函数。

Sales_item():isbn(10,‘a‘),units_sold(0),revenue(0){}

//P390 习题12.21
class DemoClass
{
public:
    DemoClass():str("DemoClass"),ival(0),pdou(0),in(inFile){}

private:
    const string str;
    int ival;
    double *pdou;
    ifstream ∈
};

//习题12.23
class NoDefault
{
public:
    NoDefault(int);
};

class C
{
public:
    C(int ival):no(ival){}

private:
    NoDefault no;
};
时间: 2024-08-02 06:54:54

C++ Primer 学习笔记_51_类与数据抽象 --构造函数【上】的相关文章

C++ Primer 学习笔记_52_类与数据抽象 --构造函数【下】

类 --构造函数[下] 二.默认实参与构造函数 一个重载构造函数: Sales_item():units_sold(0),revenue(0){} Sales_item(const std::string &book): isbn(book),units_sold(0),revenue(0) {} 可以通过给string初始化式提供一个默认实参将这些构造函数组合起来: Sales_item(const string &book = " "): isbn(book),un

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

C++ Primer 学习笔记_56_类与数据抽象 --消息处理示例

复制控制 --消息处理示例 说明: 有些类为了做一些工作需要对复制进行控制.为了给出这样的例子,我们将概略定义两个类,这两个类可用于邮件处理应用程序.Message类和 Folder类分别表示电子邮件(或其他)消息和消息所出现的目录,一个给定消息可以出现在多个目录中.Message上有 save和 remove操作,用于在指定Folder中保存或删除该消息. 数据结构: 对每个Message,我们并不是在每个Folder中都存放一个副本,而是使每个Message保存一个指针集(set),set中

C++ Primer 学习笔记_57_类与数据抽象 --管理指针成员

复制控制 --管理指针成员 引言: 包含指针的类需要特别注意复制控制,原因是复制指针时只是复制了指针中的地址,而不会复制指针指向的对象! 将一个指针复制到另一个指针时,两个指针指向同一对象.当两个指针指向同一对象时,可能使用任一指针改变基础对象.类似地,很可能一个指针删除了一对象时,另一指针的用户还认为基础对象仍然存在.指针成员默认具有与指针对象同样的行为. 大多数C++类采用以下三种方法之一管理指针成员: 1)指针成员采取常规指针型行为:这样的类具有指针的所有缺陷但无需特殊的复制控制! 2)类

C++ Primer 学习笔记_55_类与数据抽象 --析构函数

复制控制 --析构函数 引言: 在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源.析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为类构造函数的补充. 1.何时调用析构函数 撤销类对象时会自动调用析构函数: Sales_item *p = new Sales_item; { Sales_item item(*p); //调用复制构造函数 delete p; //调用指针p的析构函数 } //调用对象item的析构函数 动态分配的对象只有在指向该对象的指针被删除时才撤销,

C++ Primer 学习笔记_53_类与数据抽象 --友元、static成员

类 --友元.static成员 一.友元 友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类(对未被授权的函数或类,则阻止其访问):友元的声明以关键字friend开始,但是它只能出现在类定义的内部.友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员,所以它们不受其声明出现部分的访问控制影响. [最佳实践] 通常,将友元声明成组的放在类定义的开始或结尾是个好主意! 1.友元关系:一个例子 假设一个窗口管理类Window_Mgr可能需要访问由其管理的Screen对象的内部

C++ Primer 学习笔记_54_类与数据抽象 --复制构造函数、赋值操作符

复制控制 --复制构造函数.赋值操作符 引言: 当定义一个新类型时,需要显式或隐式地指定复制.赋值和撤销该类型的对象时会发生什么– 复制构造函数.赋值操作符和析构函数的作用!      复制构造函数:具有单个形参,该形参(常用const修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式的使用复制构造函数:当将该类型的对象传递给函数或者从函数返回该类型的对象时,将隐式使用复制构造函数.     析构函数:作为构造函数的互补,当对象超出作用域或动态分配的对象被删除

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式、auto_ptr与单例模式、const成员函数、const 对象、mutable修饰符

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式.auto_ptr与单例模式.const成员函数.const 对象.mutable修饰符 前言 [例]写出面向对象的五个基本原则? 解答:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则和里氏替换原则 里氏替换原则:子类型必须能够替换他们的基类型. 设计模式分为三种类型:创建型模式.结构型模式和行为型模式 一.static 与单例模式 1.单例模式 单例模式的意图:保证一个类仅有一个实例,并提供一个访问它

C++ Primer 学习笔记_16_类与数据抽象(2)_隐含的this指针

C++ Primer 学习笔记_16_类与数据抽象(2)_隐含的this指针 1.引言 在前面提到过,成员函数具有一个附加的隐含形参,即指向该类对象的一个指针.这个隐含形参命名为this. 2.返回*this 成员函数有一个隐含的附加形参,即指向该对象的指针,这个隐含的形参叫做this指针(编译器自动传递)使用this指针保证了每个对象可以拥有不同数值的数据成员,但处理这些成员的代码可以被所有对象共享.成员函数是只读的代码,由所有对象共享,并不占对象的存储空间,因为this指针指向当前对象,所以