原创 【博客大赛】《C++ Primer》学习笔记(24)类

2016-3-24 20:42 1595 12 13 分类: MCU/ 嵌入式 文集: Qt和Cpp
7.4 Class Scope
----------------------------------------------------------------
void Window_mgr::clear(ScreenIndex i)
{
    Screen &s = screens;
    s.contents = string(s.height * s.width, ' ');
}
此函数中的Window_mgr,说明这个函数都在它的scope里。
如果函数的返回值要使用Window_mgr中的类型的话,必须加上Window_mgr的specify。
比如:
Window_mgr::ScreenIndex Window_mgr::addScreen(const Screen &s);

编译器只有在找到所以类的成员声明之后,才会去处理成员函数的定义。
编译器会现在class里面找,然后在enclosing scope里找,再找不到就会报错。
在类的内部和外部,变量可以重名,但是typedef的定义不能够重名,否则就会报错。

编译器如何处理成员函数内部的名称?
 • 在成员函数内部寻找,且仅仅在使用这个名称之前的代码。
 • 在类里面找。
 • 在这个类前面的scope中找。
如果我们想使用类成员的名称,而不是成员函数里的本地名称,可以通过this或者类名访问:
this->height;
Screen::height;
如果我们想使用类外面的全局名称,就使用"::"这个符号,它意味着全局名称:
::height

7.5 Constructors Revisited
----------------------------------------------------------------
如果在定义一个变量时,是先定义再赋值,那么这个变量会先被赋为默认值,
因此不如在初始化的时候顺便把值给初始化了:
string foo = "Hello World!";
构造函数也是如此,与其在内部赋值,不如在它的参数列表里面指定其成员的初始值。
const类型的变量/引用/指针,以及没有默认构造函数的类,必须在参数列表里被初始化。

在构造函数的参数列表里,成员初始化的顺便并不是列表的顺序,而是它们在类中的存储顺序。
因此,最好按照类中的存储顺序来初始化成员变量,并且不要使用成员变量来初始化另一个成员变量。

Sales_data(std::string s = ""): bookNo(s) { }
Sales_data(std::string s, unsigned cnt, double rev):
    bookNo(s), units_sold(cnt), revenue(rev*cnt) { }
Sales_data(std::istream &is) { read(is, *this); }

第一个函数是默认的构造函数,因为它提供的参数列表和默认的参数列表并没有区别。
我们也可以使用空列表来调用它。

----------------
Delegating Constructors
使用其他构造函数来形成自己的构造函数:
Sales_data(std::string s, unsigned cnt, double price):
    bookNo(s), units_sold(cnt), revenue(cnt*price) {}
Sales_data(): Sales_data("", 0, 0) {}
Sales_data(std::string s): Sales_data(s, 0,0) {}
Sales_data(std::istream &is): Sales_data() {read(is, *this);}

此时,被delegating的构造函数的参数列表及函数体都会被执行,它们执行完了之后,
delegating函数的函数体会接着执行。

----------------
The Role of the Default Constructor
什么时候会用到默认的构造函数?
 • 在block scope中定义nonstatic变量,且没有初始化它。
 • 类包含有synthesize default constructor的成员类。
 • 当构造函数没有显式的初始化每一个成员变量时。
 • 当没有提供完整的数组初始化个数时。
 • 当定义一个本地的static对象且没有初始化时。
 • 当初始化类似vector的变量时,T()使用了类作为其成员。

----------------
Implicit Class-Type Conversions
不仅仅是build-in类型可以进行转换,class类型也可以。
单参数的构造函数,就代表了一个类型转换,因此这类函数也称为converting constructors。

但要注意的是,转换函数只能接受一次转换,如果构造函数接收string作为参数,那就不能使用:
combine("9-999-99999");

如果在构造函数前加上explicit关键字,意味着我们不能使用它来行类型的隐式转换。
explicit只需要用在单参数的converting constructors上;
explicit只能用在类的内部,不能在类的外部使用;
explicit只能用在直接的initialization上:
Sales_data item1(null_book);   // OK
Sales_data item1 = null_book;  // ERROR

----------------
Aggregate Classes
我们可以直接访问它的成员:
struct Data {
    int ival;
    string s;
};
Data val1 = {0, "Anna"};

这种直接访问类成员的方式看其来很简单,实际上有很多缺点:
 • 所有的成员变量必须是public。
 • 类的使用者必须负责进行冗长且乏味的赋值操作。
 • 一旦类修改,所有的赋值操作都要修改。

----------------
Literal Classes
一个aggregate class,如果它的成员变量都是literal类型的,就可以称它literal class。
一个非aggregate class,满足如下要求的也是literal class:
 • 所有的成员都是literal类型;
 • 至少有一个constexpr构造函数;
 • in-class的初始化必须是constant表达式,或者成员类有constexpr构造函数;
 • 必须使用默认的析构函数。

7.6 Class Members
----------------------------------------------------------------
static关键字,使得类的成员不再属于某个单独的类对象,而是属于整个类。
被static修饰的成员,可以是const/引用/数组/clas或其他。
被static修饰的成员,可以是private或者public。

class Account {
public:
    void calculate() { amount += amount * interestRate; }
    static double rate() { return interestRate; }
    static void rate(double);
private:
    std::string owner;
    double amount;
    static double interestRate;
    static double initRate();
};

每个Account个体仅仅包含owner和amount两个成员,其他被static修饰的都是共用的。
被static修饰的函数,不会有this指针。
被static修饰的函数,也不会是const的。

我们可以通过类的类型名直接访问static变量或函数:
double r;
r = Accout::rate();

当然我们仍然可以通过具体的对象来访问它们。
成员函数可以直接访问被static修饰的成员变量。

在类的外部定义static函数时,不需要使用static修饰,在类的内部做就可以了。
类的构造函数不会去处理static成员变量,同理我们不能在类的内部去处理static变量。
static变量必须在类的外部进行定义和处理,就像是全局变量一样。
为了保证static成员变量只被定义一次,我们通常将它们放置在对应的源文件中。

一般来说,static成员变量不能在类的内部进行初始化,但也有例外的情况:
如果static成员是const integral类型,就可以在内部进行。
如果static成员只用在了编译器可直接使用它的值的地方,就无需在外面再定义,否则就需要。
如果static成员进行了类内部的初始化,外部的定义就不能再赋值了。

static成员定义在了类外面,因此它的数据类型可以是这个类本身。
static成员可以作为构造函数的参数。

文章评论1条评论)

登录后参与讨论

用户593939 2016-3-28 18:03

学习了,参考了
相关推荐阅读
DiracFatCat 2018-09-05 12:14
【博客大赛】卡尔曼滤波学习笔记(11)从位置估计速度
卡尔曼滤波器,不仅仅是一个低通滤波器,否则也不会持续发展50年。 示例:桑先生需要测试高速列车的性能。测试的目的是判断列车在直线上能否保持80m/s的速度。速度和位置每0.1秒测量一次,但是由于...
DiracFatCat 2018-08-31 19:32
【博客大赛】卡尔曼滤波学习笔记(10)一个简单的示例
《Kalman Filtering: Theory and Practice Using MATLAB》第三章,看不懂,暂时略过。《Kalman Filtering: Theory and Pract...
DiracFatCat 2018-07-19 15:09
对sed命令的练习
sed是流编辑器。它每次处理一个输入,因此很有效率。官方手册:https://www.gnu.org/software/sed/manual/sed.html学习Linux命令,当然要阅读官方手册,所...
DiracFatCat 2018-06-19 15:10
【博客大赛】卡尔曼滤波学习笔记(八)可观测性和可控制性 ...
可观测性是指,在给定模型的情况下,动力学系统的状态是否由它的输入输出唯一确定。可观测性是系统模型的特征。如果传感器矩阵H是可逆的,则本系统可观测,因为有:如果传感器矩阵H某些时候是不可逆的,则本系统仍...
DiracFatCat 2018-06-19 10:56
【博客大赛】卡尔曼滤波学习笔记(七)Z变换
如果我们仅仅对离散线性系统感兴趣,那么就使用下面这个表达式:如果u是常量,那么可以写成:为了简化表达式,我们可以将上面写成:离散线性时不变系统的Φ求解,可以使用Z变换。(* 由于本人已经忘记了Z变换的...
DiracFatCat 2018-06-19 10:54
【博客大赛】卡尔曼滤波学习笔记(六)拉普拉斯变换
对于线性时变/时不变系统,求解Φ(t)有多种方式,拉普拉斯变换是其中一种。(* 由于本人已经忘记了拉普拉斯变换的内容,因此本节待续。)...
我要评论
1
12
关闭 站长推荐上一条 /2 下一条