PS,1880后程序员

看不完的牙,写不完的程序,跑不完的步。
随笔 - 97, 文章 - 34, 评论 - 10, 引用 - 0
数据加载中……

C++ Primer 之 读书笔记 第十二章

 

Classes

12.1. 类的定义和声明 Class Definitions and Declarations

类定义

大师仅仅写了这么一个类定义的简单的例子,却有这么多的东西可以说说啊

class Sales_item {

public:

    // operations on Sales_item objects

    double avg_price() const;

    bool same_isbn(const Sales_item &rhs) const

        { return isbn == rhs.isbn; }

    // default constructor needed to initialize members of built-in type

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

private:

    std::string isbn;

    unsigned units_sold;

    double revenue;

};

double Sales_item::avg_price() const

{

    if (units_sold)

        return revenue/units_sold;

    else

        return 0;

}

·       Member: data, functions or type definitions.

·       构造函数Constructors初始化:

// default constructor needed to initialize members of built-in type

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

这种写法是初始化内置数据类型。

·                 const:表示functions不会修改数据成员。这可以用在查询或者判断类型的functions上。

·       成员函数member functions必须在类的内部声明,成员函数的定义是否放在类定义里面是可选的。如果放在类里面,那么

数据抽象和封装Data Abstraction and Encapsulation

类背后蕴涵的基本思想是数据抽象和封装。(The fundamental ideas behind classes are data abstraction and encapsulation.)数据抽象这种技术依赖于接口和实现的分离。(Data abstraction is a programming (and design) technique that relies on the separation of interface and implementation.

多么精炼的话,包含了面向对象的深刻含义。大师啊。

类的用户(user of class)是谁?程序员programmeruser of class就如同user of application

类的用户最关心啥?最关心的是接口。

关于类定义的更多内容 more on class definitions

使用Typedef简化类

class Screen {

public:

    // interface member functions

    typedef std::string::size_type index;

private:

    std::string contents;

    index cursor;

    index height, width;

};

注意:

typedef是放在public部分的,这样Screen的用户都能够使用这个typedef

成员函数可被重载Member Functions May Be Overloaded

显式指定 inline 成员函数Explicitly Specifying inline Member Functions

成员函数定义为inline。这个定义即可以在函数声明里,也可以在类定义之外。在类声明和类定义里把函数指定为inline都是合法的。(It is legal to specify inline both on the declaration and definition.

类声明与类定义

类定义一般要放在头文件中。

类声明后,即使没有类定义,也可以作为引用或者指针来使用:

class LinkScreen {

       Screen window;

       LinkScreen *next;

       LinkScreen *prev;

};

LinkScreen定义完成之前,我们就定义了指向LinkScreen类型的指针。

这里引入了两个基本感念:

前向声明forward declaretion

class Screen; //definition of the Screen class

这就是一个前向声明。表示Screen是一个类类型(class type)。

不完全类型incompete type:在前向声明之后,类定义之前Screen就是一个不完全类型。意思是说知道Screen是个类型,但是不知道这个类型包括那些成员(members)。不完全类型只能用在有限的几种方式,它可以用来定义这种类型的指针或者是引用,也可以用在把这种类类型作为形参或者返回值类型的函数声明。(An incomplete type may be used to define only pointers or references to the type or to declare (but not define) functions that use the type as a paremeter or return type.

在创建某个类类型的对象前,这个类类型必须完整定义。这样编译器餐能够知道要为这种类型的对象保留多大的存储空间。

12.2 隐含的this指针 The Implicit this Pointer

何时使用this

简单的说,就是当我们需要引用整个对象而不是对象中的成员的时候。最常见的情况是在返回值是被调用的对象的引用的函数里。(when we need to refer to the object as a whole rather than to a member of the object. The most common case where we must use this is in functions that return a reference to the object on which they were invoked.

this是指针,因此如果返回值是当前对象的引用,那么返回语句一定是:

return *this;

刚开始看到这个的时候,总是和Java搞混,因为Java是没有指针,所以返回的只能是this,显式使用的时候也是this

const成员函数里返回*this

这里有两个规则:

  1. 对于一般的非const成员函数,this的类型是一个const pointer。这意味我们可以修改this指向的对象的值,但是不能修改this保存的地址。(In an ordinary nonconst member function, the type of this is a const pointer to the class type. We may change the value to which this points but cannot change the address that this holds.
  2. 对于const成员函数,this的类型是一个指向const 对象的const pointer。这意味我们既不可以修改this指向的对象的值,也不能修改this保存的地址。(In a const member function, the type of this is a const pointer to a const class-type object. We may change neither the object to which this points nor the address that this holds.

基于const的重载

可以定义两个操作,一个是const,另一个不是,这也算是重载。

class Screen {

 public:

     // interface member functions

     // display overloaded on whether the object is const or not

     Screen& display(std::ostream &os)

                   { do_display(os); return *this; }

     const Screen& display(std::ostream &os) const

                   { do_display(os); return *this; }

 private:

      // single function to do the work of displaying a Screen,

      // will be called by the display operations

      void do_display(std::ostream &os) const

                        { os << contents; }

      // as before

 };

这样const对象使用const成员函数;非const对象可以使用任一成员函数,不过最好还是使用非const的成员函数。

对比下面的例子(基于指针形参是否指向const的函数重载):

Record lookup(Account&);

Record lookup(const Account&); // new function

可变数据成员Mutable Data Members

即使对象是const,有时我们也希望其中的某些数据成员是可以修改的,这就引出了mutable

可变数据成员绝不是const,即使对象是const。这样const的成员函数也是可以修改可变数据成员的。

定义:

class Screen {

 public:

 // interface member functions

 private:

     mutable size_t access_ctr; // may change in a const members

     // other data members as before

 };

12.3 类的作用范围

12.4构造函数Constructors

构造函数不能声明为const

建议的方法:使用构造函数初始化式Constructor Initializer

// recommended way to write constructors using a constructor initializer

Sales_item::Sales_item(const string &book):

     isbn(book), units_sold(0), revenue(0.0) { }

Constructor Initializer

以下是正确的写法:

// recommended way to write constructors using a constructor initializer

Sales_item::Sales_item(const string &book):

     isbn(book), units_sold(0), revenue(0.0) { }

但是这种写法乍一看似乎和下面不使用Constructor Initializer的效果一样:

// legal but sloppier way to write the constructor:

// no constructor initializer

Sales_item::Sales_item(const string &book)

{

    isbn = book;

    units_sold = 0;

    revenue = 0.0;

}

NONO,不一样。后一种写法那就像郭德纲相声说的房都烧没了,就剩下个防盗门,于谦还开门进去。isbn的初始化是在构造函数执行之前完成的。上面这个写法先用缺省的string的构造函数初始化isbn,在构造函数的函数体执行时,isbn又重新赋值为book。没效率啊!但是对于内置类型来说,两种方式无论是在结果上还是在性能上又都是没有区别的。

构造函数的执行分成两个步骤:

  1. 初始化阶段,类的数据成员是在这个阶段初始化的。
  2. 一般计算阶段,这就是指构造函数函数体的执行。

在计算阶段前初始化阶段就开始了。(Initialization happens before the computation phase begins.

在一些特定的情况下必须使用Constructor Initializer,这些特殊的情况包括:

  1. 数据成员没有缺省的构造函数
  2. 数据成员是const或者是引用

for example:

// legal but sloppier way to write the constructor:

// no constructor initializer

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)

 {              // assignments:

      i = ii;   // ok

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

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

 }

正确写法:只有用constructor initializer才有机会对const和引用进行初始化。

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

成员初始化顺序

成员的初始化顺序就是成员的定义的顺序。第一个定义的成员第一个初始化,然后是下一个,以此类推。(The order in which members are initialized is the order in which the members are defined. The first member is initialized first, then the next, and so on.

类类型的数据成员的初始化Initializers for Data Members of Class Type

Constructor Initializer初始化式使用类的某一个构造函数来完成。

// alternative definition for Sales_item default constructor

Sales_item(): isbn(10, '9'), units_sold(0), revenue(0.0) {}

缺省构造函数

只有在类没有定义任何一个构造函数时,编译器才会自动生成缺省的构造函数。

隐式类类型转换 Implicit Class-Type Conversions

12.5. 友元Friends

友元就是允许其它特定的类和方法来访问它的非public的成员。(The friend mechanism allows a class to grant access to its nonpublic members to specified functions or classes.

友元定义:

class Screen {

     // Window_Mgr members can access private parts of class Screen

     friend class Window_Mgr;

     // ...rest of the Screen class

 };

友元不是类的成员,但是他们是类的接口的组成部分。(even if they are not members of the class, they are "part of the interface" to the class.

友元可以是:

类的成员函数

class Screen {

// Window_Mgr must be defined before class Screen

friend Window_Mgr&

     Window_Mgr::relocate(Screen::index r, Screen::index c,

                      Screen& s);

     // ...rest of the Screen class

 }

一般的非成员函数

友元声明和范围

友元声明和友元定义之间存在的相互依赖关系使得我们小心地构造类。再看上面的例子:

Window_Mgr必须要的Screen前定义。但是成员方法relocate要在Screen定义后才能定义。靠,这就像是先有蛋还是先有鸡一样拧巴。

class Screen {

// Window_Mgr must be defined before class Screen

friend Window_Mgr&

     Window_Mgr::relocate(Screen::index r, Screen::index c,

                      Screen& s);

     // ...rest of the Screen class

 }

12.6. static 类成员static Class Members

这句话是对static class member的最重要的解释:

静态数据成员独立于类的任何对象而存在,每个静态数据成员是和类相关的对象,而不是和类的对象相关。(a static data member exists independently of any object of its class; each static data member is an object associated with the class, not with the objects of that class.

使用静态数据成员的好处

static成员的名字的作用范围是类,因此可以避免和其它的类的成员或者全局对象的名字的冲突。

强制进行封装。static成员可以是private的,而全局对象是不可以的。

如果把static成员关联到某个特殊的类上,这样易于阅读代码。

静态成员函数static member function

在静态成员函数(static member function)中是不能使用this指针的。这其实很好理解,因为this指针指向的是对象,而静态成员函数并不属于任何的对象。

静态数据成员static data members

静态数据成员可以是任何的类型:const,引用reference,数组,指针类。

class Account {

public:

    // interface functions here

    void applyint() { amount += amount * interestRate; }

    static double rate() { return interestRate; }

    static void rate(double); // sets a new rate

private:

    std::string owner;

    double amount;

    static double interestRate; //这是声明

    static double initRate();

};

// define and initialize static class member,这是定义

double Account::interestRate = initRate();

以上是一个完整的static的定义,要注意以下几点:

1.      静态数据成员要在类定义体之外来定义。(static data members must be defined (exactly once) outside the class body.

2.      静态成员不能通过构造函数进行初始化,而是在定义的时候就完成了初始化。(static members are not initialized through the class constructor(s) and instead should be initialized when they are defined.

3.      关键字static只是用在类体内的成员声明,而当在类的外面的对静态数据成员进行定义时,就不要再使用关键字static。(The static keyword, however, is used only on the declaration inside the class body. Definitions are not labeled static.

整型类型的静态数据成员可以在类定义体内直接定义,只要是初始化值是常量表达式。

a const static data member of integral type can be initialized within the class body as long as the initializer is a constant expression

period的定义就是这样:

class Account {

 public:

      static double rate() { return interestRate; }

      static void rate(double); // sets a new rate

 private:

      static const int period = 30; // interest posted every 30 days

      double daily_tbl[period]; // ok: period is constant expression

 };

// definition of static member with no initializer;

// the initial value is specified inside the class definition

const int Account::period;

注意:虽然period在类定义体内完成了初始化,但是还是要在类定义之外定义数据成员。(When a const static data member is initialized in the class body, the data member must still be defined outside the class definition.

静态成员不是类对象的一部分

也正因为如此,对于非静态成员的非法的使用方式,对于静态成员来说就是合法的。例如:静态成员的类型可以是它所属类的类型。非静态数据成员被严格限制只能是它所属类的对象的引用或者是指针。(the type of a static data member can be the class type of which it is a member. A nonstatic data member is restricted to being declared as a pointer or a reference to an object of its class:

class Bar {

 public:

      // ...

 private:

      static Bar mem1; // ok

      Bar *mem2;       // ok

      Bar mem3;        // error

 };

posted on 2009-06-16 09:17 amenglai 阅读(844) 评论(1)  编辑  收藏 所属分类: C++ Primer 之 读书笔记

评论

# re: C++ Primer 之 读书笔记 第十二章   回复  更多评论   

你好,看到你的博客写了四年,我觉得楼主肯定是一位真正的大牛和一个懂得坚持的人。我先在在看C++ primer,类以后我很是惧怕,不知道前辈能否指点一二,讲讲自己的读书心得!
2013-09-14 21:56 | 水目沾

只有注册用户登录后才能发表评论。


网站导航: