《C++Primer》 类

● 类的定义和声明

Sales_item():units_sold(0),revenue(0.0){}

构造函数初始化列表由成员名和带括号的初始值组成,跟在构造函数的形参表之后,并以冒号开头。

 

在类内部定义的函数默认为inline。

 

将关键字const’加在形参表之后,就可以将成员函数声明为常量。

double avg_proce() const;

const成员不能改变其所操作的对象的数据成员。const必须同时出现在声明和定义中。

 

使用类型别名来简化类:

class Screen{

public:

     typedef std::string::size_type index;

private:

     index cursor;

     index width,height;

}

 

● 隐含的this指针

类中的成员函数具有一个附加的隐含形参,即指向该类对象的一个指针。该隐含形参命名为this,与调用成员函数的对象绑定在一起。在成员函数内部显式引用this通常是不必要的。但一种情况下我们必须这么做:当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。

最常见:该函数返回调用该函数的对象的引用

class screen {};

screen& move() {

return *this;

}

从const函数返回*this

非const成员函数中,this的类型是一个指向类类型的const指针,可以改变this所指向的值,但不能改变this所保存的地址。在const成员函数中,this的类型是一个指向const类类型对象的const指针 。既不能改变this所指向的对象,也不能改变this所保存的地址。

不能从const成员函数返回指向类对象的普通引用。const成员函数只能返回*this作为一个const引用。

 

基于const的重载

基于成员函数是否为const,可以重载一个成员函数,同样地,基于一个指针形参是否指向const,可以重载一个函数。

 

mutable定义可变成员

可变数据成员永远都不能为const,甚至当它是const对象的成员也如此。

任意成员函数,包括const函数,都可以改变可变成员的值。

 

● 类作用域

形参表和函数体处于类作用域中。

函数返回类型不一定在类作用域中。

如果函数在类定义体之外定义,则用于返回类型的名字在类作用域之外。如果返回类型使用由类定义的类型,则必须使用完全限定名。

inline Screen::index Screen::getCursor() const

{

     return cursor;

}

 

类成员定义中的名字查找

1.首先检查成员函数局部作用域中的声明。

2.如果在成员函数中找不到该名字的声明,则检查对所有类成员的声明。

3.如果在类中找不到该名字的声明,则检查在此成员函数定义之前的作用域中出现的声明。

 

● 构造函数

构造函数不能声明为const。

 

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

Sales_item::Sales_teim(const string &book):isbn(book), units_sold(0), revenue(0.0) {}

构造函数初始化列表难以理解的一个原因在于,省略初始化列表并在构造函数的函数体内对数据成员赋值是合法的。

Sales_item::Sales_teim(const string &book)

{

      isbn  = book;

      units_sold = 0;

      revenue = 0.0;

}

这个构造函数给类Sales_item的成员赋值,但没有进行显式初始化。不管是否有显式的初始化式,在执行构造函数之前,要初始化isbn成员。这个构造函数隐式使用默认的string构造函数来初始化isbn。执行构造函数的函数体时,isbn成员已经有值了。该值被构造函数函数体中的赋值所覆盖。

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

1.初始化阶段。

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

 

如果没有为类成员提供初始化式,则编译器会隐式地使用成员类型的默认构造函数。如果那个类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。在这种情况下,为了初始化数据成员,必须提供初始化式。

 

没有默认构造函数的类类型的成员,以及const或引用类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。

 

下面的构造函数是错误的:

class ConstRef{

public:

    ConstRef(int ii);

private:

    int i;

    const int ci;

    int &ri;

};

//no explicit constructor initializer:error ri is uninitialized

ConstRef::ConstRef(int ii)

{

    i = ii;    //ok

    ci = ii;  //error:cannot assign to a const

    ri = i;   //assigns to ti which was not bound to an object

}

 

正确的方式应该为:

ConstRef::ConstRef(int ii):i(ii),ci(i),ri(ii){ }

 

成员被初始化的次序就是定义成员的次序。

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

class X{

     int i;

     int j;

public:

    X(int val):j(val) , i(j){}       //run-time error:i is initialized before j

}

 

在这种情况下,构造函数初始化列表看起来似乎是用val初始化j,然后再用j来初始化i。然后,i首先被初始化,这个初始化列表的效果是用尚未初始化的j值来初始化i。

因此,尽量避免使用成员来初始化其他成员。

 

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

Sales_item(): isbn(10, ‘9’),units_sold(0),revence(0.0){ }   //isbn is a string

 

Sales_item myobj();  //declares a function, not an object

Sales_item myobj;

Sales_item myobj = Sales_item();

 

将构造函数声明为explicit,来防止在需要隐式转换的上下文中使用构造函数:

explicit关键字只能用于类内部的构造函数声明上,在类定义体外部所做的定义上不再重复它。

 

class Sales_item{

public:

    explicit Sales_item(const std::string &book=””):isbn(book),units_sold(0),revence(0.0){}

}

 

string null_book = “9-999-999-9-9”;

item.same_item(null_book);   /error: string construct is explicit

item.same_item(Sales_item(null_book));   //is ok

 

因为same_item函数需要一个Sales_item对象的实参。

 

● 友元

友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。

友元的声明以关键字friend开始,它只能出现在类定义的内部。友元可以是普通的非成员函数,或其他类的成员函数,或整个类。将一个类设为友元,友元类的所有成员函数都可以访问授予友元关系的那个类的非公有成员。

 

class Screen{

  friend WindowMgr& WindowMgr::relocate(WindowMgr::index,WindowMgr::index,Screen&);

}

 

● static 类成员

1. 通常,非static数据成员存在于类类型的每个对象中。而static数据成员独立于该类的任意对象而存在,每个static数据成员是与类关联的对象,而不是与该类的对象相关联。

2. 类可以定义共享的static数据成员,类也可以定义static成员函数。static成员函数没有this形参,它可以直接访问所属类的static成员,但不能直接访问使用非static成员。

3. 定义static成员:加上关键字static将成员设为static。static成员遵循正常的公有/私有访问规则。

4. 使用类的static成员:

    通过作用域操作符从类直接调用static成员,或者通过对象、引用或指向该类类型对象的指针间接调用。

    类成员函数可以不用作用域操作符来引用类的static成员。

5. static成员函数:

    当我们在类的外部定义(定义,而不是声明)static成员时,无须重复指定static保留字,该保留字只出现在类定义体内部的声明处。

    static成员函数不能被声明为const。毕竟,将成员函数声明为const就是承诺不会修改该函数所属的对象。

    static成员函数不能被声明为虚函数。

6. static数据成员:

    static数据成员必须在类定义体的外部定义(正好一次),不像普通数据成员,static成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化(通常将static数据成员的定义放在包含类的非内联成员函数定义的文件中)。

一般而言,类的static成员不能在类的定义体初始化,而是在定义时才初始化。但只要初始化式是一个常量表达式,整型const static数据成员就可以在类的定义体中进行初始化,但是,该数据成员仍必须在类的定义体之外进行定义.(~)

 

static数据成员的类型可以是该成员所属的类类型,非static成员被限定声明为其自身类对象的指针或引用:

class Bar{

private:

static Bar mem1;   //ok

Bar  *mem2;   //ok

Bar   mem3;   //error

};

 

类似的,static数据成员可用作默认实参。

 

class Screen{

public:

    Screen& clear(char = bkground);

privte:

static const char bkground = ‘#’;

};

非static数据成员不能用作默认实参。

 

BY:AloneMonkey

本文链接:http://www.alonemonkey.com/cplus-review-seven.html