c++primer plus第十四章-valarray类
1)模板特性意味着声明对象时,必须指定具体的数据类型。因此,使用valarray类来声明一个对象时,需要在标识符valarray后面加上一对尖括号,并在其中包括所需的数据类型。
valarray<int> q_values;
valarray<double> weights;
2)可以创建长度为0的空数组、指定长度的空数组、所有元素被初始化为指定值的数组、用常规数组中的值进行初始化的数组。也可以使用初始化列表:
valarray<int> v5={20,32,17,9};
operator[]():让你可以访问各个元素;
size():返回包含的元素数;
sum():返回所有元素的总和;
max():返回最大的元素;
min():返回最小的元素;
3)关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。声明为explicit的构造函数不能在隐式转换中使用。
C++中, 一个参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。 1 是个构造器, 2 是个默认且隐含的类型转换操作符。
所以,有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型,这时候编译器就自动调用这个构造器, 创建一个AAA的对象。
这样看起来好象很酷, 很方便。 但在某些情况下(见下面权威的例子), 却违背了我们(程序员)的本意。这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用,使用, 不能作为类型转换操作符被隐含的使用。
explicit构造函数的作用:explicit构造函数是用来防止隐式转换的。请看下面的代码:
class Test1
{
public:
Test1(int n) { num = n; } //普通构造函数
private:
int num;
};
class Test2
{
public:
explicit Test2(int n) { num = n; } //explicit(显式)构造函数
private:
int num;
};
int main()
{
Test1 t1 = 12; //隐式调用其构造函数, 成功
Test2 t2 = 12; //编译错误,不能隐式调用其构造函数
Test2 t3(12); //显示调用成功
return 0;
}
Test1的构造函数带一个int型的参数,代码会隐式转换成调用Test1的这个构造函数。而Test2的构造函数被声明为explicit(显式),这表示不能通过隐式转换来调用这个构造函数,因此代码会出现编译错误。
普通构造函数能够被隐式调用。而explicit构造函数只能被显示调用。
4)初始化顺序:当初始化列表包含多个项目时,这些项目被初始化的顺序为它们被声明的顺序,而不是它们在初始化列表中的顺序。
class Student
{
private:
string name;
valarray<double> scores;
... ...
}
Student(const char *str, const double *pd, int n)::scores(pd, n), name(str){... ...}//Student 的构造函数
则name成员仍然首先被初始化,因为在类定义中它首先被定义。对于这个例子来说,初始化顺序并不重要。但是如果代码使用一个成员的值作为另一个成员的初始化表达式的一部分时,初始化顺序就非常重要了。
5)私有继承
使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会成为派生类对象公有接口的一部分,但是可以在派生类的成员函数中使用它们。
包含将对象作为一个命名的成员对象添加到类中,而私有继承将对象作为一个未命名的继承对象添加到类中。我们使用术语子对象来表示通过继承或包含添加的对象。
因为私有继承提供的特性与包含相同:获得实现,但不获得接口。
6)多重继承,初始化基类组件
对于继承类,新版本的构造函数将使用成员初始化列表语法,它使用类名而不是成员名来标识构造函数:
Student(const char *str, const double *pd, int n):name(str), scores(pd, n){}//包含的构造函数。
Student(const char *str, const double *pd, int n):std::string(str), ArrayDb(pd, n){}//私有继承的构造函数
7)访问基类的方法
使用私有继承时,只能在派生类的方法中使用基类的方法。使用包含时将使用对象名来调用方法,然而,私有继承使得能够使用类名和作用域解析运算符来调用基类的方法。
8)访问基类对象
使用作用域解析运算符可以访问基类的方法,但是如果要使用基类对象本身呢,答案是使用强制类型转换。指针this指向用来调用方法的对象,因此*this为用来调用方法的对象。使用强制类型转换来创建一个引用:
const string & Student :: Name() const
{
return (const string &) *this;
}
该方法返回一个引用,该引用指向用于调用该方法的Student对象中的继承而来的string对象。
9)访问基类的友元函数
10)保护继承
使用保护继承时,基类的公有成员和保护成员都将成为派生类的保护成员。和私有继承一样,基类的接口在派生类中也是可用的,但是在继承层次结构之外是不可用的。当从派生类派生出另一个类时,私有继承和保护继承之间的主要区别便呈现出来了。使用私有继承时,第三代类将不能使用基类的接口,这是因为基类的公有方法在派生类中将变成私有方法,使用保护继承时,基类的公有方法在第二代中将变成受保护的,因此第三代派生类可以使用它们。
11)各种继承方法表:
特征 公有继承 保护继承 私有继承
公有成员 派生类的公有成员 派生类的保护成员 派生类的私有成员
保护成员 派生类的保护成员 派生类的保护成员 派生类的私有成员
私有成员 只能通过基类接口访问 同左 同左
能否隐式向上转换 是 是 否