===================================
chapter 9
===================================
STL标准库提供的容器包括vector,list,deque,stack,queue,priority_queue等
------------------------------------------------------------------------------------------------------------------------------------------
stack的操作:
int _tmain(int argc, _TCHAR* argv[])//#include <stack> { stack <int> st; st.push(10); st.push(11); st.push(12); while(!st.empty()){ //判断是否为空 cout<<st.top() //读取栈顶,但不删除栈顶 <<" " <<st.size() //栈内元素总个数 <<endl; st.pop(); //删除栈顶,但不返回栈顶 } return 0; }
------------------------------------------------------------------------------------------------------------------------------------------
queue的操作:
int _tmain(int argc, _TCHAR* argv[])//#include <queue> { queue <int> q; q.push(10); q.push(11); q.push(12); //入队 while(!q.empty()){ //判断是否为空 cout<< q.front() //读取队头,但不删除队头 <<" " << q.size() //队内元素总个数 <<endl; q.pop(); //删除队头,但不返回队头 } return 0; }
可见,队列和栈stl的操作除了top()和front()不同,其他函数名和参数均一致。
------------------------------------------------------------------------------------------------------------------------------------------
优先队列:priority_queue
成员函数名和stack一致,但是概念上属于队列,包括<queue>头文件。
int _tmain(int argc, _TCHAR* argv[])//#include <queue> { priority_queue <int> st;//变量命名为pq更佳,这里只是复制了栈测试代码 st.push(10); st.push(12); st.push(11); while(!st.empty()){ //判断是否为空 cout<<st.top() //读取最高优先级元素,但不删除,默认为最大堆,即为堆顶 <<" " <<st.size() //优先队列中元素总个数 <<endl; st.pop(); //删除堆顶,但不返回堆顶 }//输出12 11 10 return 0; }
为了使用最小堆,可以再实例化模板时指定额外的参数:
priority_queue <int,vector<int>, greater<int> > pq;
第二、三个参数默认为vector<int>和less<int>。
------------------------------------------------------------------------------------------------------------------------------------------
双向链表:list
------------------------------------------------------------------------------------------------------------------------------------------
容器内元素类型必须满足的最低约束条件包括:
(1)元素类型必须支持赋值运算;(引用类型没有一般意义的赋值运算)
(2)元素类型对象必须可以复制。
标准库中,除了IO标准库类型,其他类型都可作为容器元素,包括容器本身。
当然,上述只是最低要求,满足上述要求,我们就可以定义该类型的容器,但如果需要用到该容器某些操作,则元素类型必须支持。
------------------------------------------------------------------------------------------------------------------------------------------
定义容器的容器时,注意右括号留个空格,否则会与右移操作符混淆。vector<vector<>
>
------------------------------------------------------------------------------------------------------------------------------------------
所有容器都支持的操作:
* -> ++ -- == !=
各种别名:size_type difference_type value_type
iterator reverse_iterator reference const_iterator const_reverse_iterator
const_reference
begin() end() push_back(t) insert(p,...)
---------------------------------------------------------------------
添加元素时,容器元素都是副本,改变新容器元素不会影响原元素
===================================
chapter 12
===================================
指针数组:int *p[4];
数组指针:int (*p)[4];
-----------------------------------------------------------------------
构造函数初始化表与构造函数初始化函数体的区别:
后者:先调用成员的默认初始化,再通过函数体进行第二次初始化;
前者:直接通过初始化列表进行初始化,效率高。const成员和引用只能用这种方式初始化。初始化表只指定了每个成员的初始化值,并未指定初始化顺序。
-----------------------------------------------------------------------
sale_item myobj;//调用默认构造函数
sale_item myobj();//函数定义:返回sale_item,形参为空
-----------------------------------------------------------------------
隐式类型转换:
对一个对象A赋值为B时,如果B的类型为A的某构造函数的形参,则调用A的构造函数A(B)生成A,进而完成类型转换。
此情形只适用一个参数的构造函数。
如果想抑制这种情况的出现,可在类定义{}内部的构造函数前加“explicit”关键字。
--------------------------------------------------------------------
友元:
包括友元函数和友元类;
只能在类A定义的{}内部指定,利用friend关键字指定;
友元函数或友元类的成员函数能访问A对象的所有成员,包括私有;
友元不是类A的成员,添加友元不影响A的实现位置和过程,也不影响友元的实现位置和过程。只影响友元函数体对某A对象的访问权限。
---------------------------------------------------------------------------
类static成员:
等效于类名作用域内的全局变量和全局函数;属于类不属于某个对象,所有对象共享static成员。
可通过 :: 作用域、对象引用或指针引用,但 :: 更符合static成员的概念;
static成员函数不能访问非static成员,也没有隐形的this形参,不能申明为const 和 virtual。
普通的static数据成员的初始化必须在{}外进行,它不是通过构造函数初始化的,一般放在cpp文件中,初始化时不能再有static关键字。
对于静态常量成员变量,其一般在类定义内初始化,如"class C{static const int a=0;};",且必须在外定义,不必再指定初始值,如"const int C::a;"//C.cpp
类定义中,普通成员的类型不能为类本身,只能为类的指针或引用,但static成员的类型可以为类本身,如"class A{ A *a; static A c};"
--------------------------------------------------------------------------
explicit friend static 只能出现在类定义{}内,不需也不能在函数定义时重新注明。
inline 可以出现{}内外,但inline 成员函数的定义必须在调用该函数的每个源文件中可见,编译器才会将其展开,因此,inline函数的定义一般放在头文件中。
===================================
chapter 13
===================================
对象构造函数会自动调用非静态成员中的类类型成员的构造函数,对于析构函数也一样。
---------------------------------------------------------------------------------
当程序员未显式定义时,编译器会为每个类自动定义构造函数、复制构造函数、赋值操作符和析构函数。
复制构造函数一般是,将普通构造函数构造的对象A,复制并构造出新的对象B。
复制构造函数常常发生在给函数传递非引用实参时、函数返回非引用值时......
复制构造函数说白了就是形参为该类引用的构造函数,如,sales_item::sales_item(constsales_item&),通过显式定义它可控制复制行为
如果先禁止赋值,可将复制构造函数申明为private,事实上很少这么做
---------------------------------------------------------------------------------
析构函数只能有一个,无形参无返回值。即使显式定义了析构函数,默认析构函数也会在显式析构函数运行完毕后得以运行。
===================================
chapter 14
===================================
除了函数调用操作符和后缀式自增自减操作符,重载操作符时,形参个数(包括了隐式的this)必须等于操作符操作数的个数,操作符作为成员函数时,第一个操作符就是this,this指向第一个操作数即左操作数;
函数调用操作符( "operator()" )可以有任意数目的操作数;
不可重载的操作符:::、.*、. 和 ? : ;
重载操作符至少有一个类类型或枚举类型的操作数;
重载操作符的优先级和结合性不变。
--------------------------------------------------------------------------------
重载操作符的返回值类型与普通操作符相匹配
---------------------------------------------------------------------------------
赋值操作符的重载必须为类成员函数,必须返回*this即左操作数的引用,且可以有多个不同形参的重载
定义operator[],operator*和operator->时,一般需要定义两个版本,一个返回const,一个返回非const,以保证const和非const对象的操作需求。
operator->必须定义为类成员,只接受一个this形参
--------------------------------------------------------------------------------------
定义前后缀++/--操作符时,如果按照普通规律,则两者定义形式一样,为了区分,在定义后缀++/--操作符时,接受一个额外无用的int参数:
A &operator++(){};//前缀,返回新值,故可返回*this引用
A operator++(int){}; //后缀,返回旧值,故先临时存储*this,再采用--*this,再返回临时存储的就*this
-----------------------------------------------------------------------------------
重载转换操作符时,必须是类成员,只包含一个this隐式形参,申明时无返回值,但函数体必须返回与转换操作符匹配的值:
class A{
int a;
......
operator int() const
{return a}
};
重载了类型转换的类,编译器在需要进行转换的地方会自动调用它,不必显式地进行类型转换
------------------------------------------------------------------------------
c++所有不常见关键字:
asm auto dynamic_cast typeid typename enum mutable explicit static_cast export template const_cast volatile throw wchar_t register reinterpret_cast
------------------------------------------------------------------------------
const_cast<type>(exp):转换表达式exp的const属性,避免使用;
static_cast<type>(exp):任何编译器隐式执行的任何类型转换都可以用它显示完成;其类型信息在编译时静态处理;
reinterpret_cast<type>(exp):实现底层比特级的强制转换,避免使用
dynamic_cast<type>(exp):适用于有一个或多个虚函数的类,在运行时,将基类类型的指针或引用安全地转换为派生类的指针或引用。
===================================
chapter 15
===================================
派生类必须对父类虚函数进行申明,如果没有定义,则会使用基类版本
基类一旦定义某函数为虚函数,则它永远为虚函数,派生类申明时,可以不加virtual关键字
派生类虚函数定义方式必须与基类保持一致,只有一种例外:函数返回类型为基类引用或指针时,其返回类型可改为派生类的引用或指针。
对象的引用和指针具有多态性,但对象本身(如 class A a)是非多态的,运行的函数由对象的类型静态决定
-----------------------------------------------------------------------
派生类继承的基类成员的访问权限由基类定义的访问级别和派生列表指定的访问级别共同决定。
派生列表指定的访问级别只可以进一步限制但不能放松对继承成员的访问级别
public继承:在派生类中,基类成员保持原访问级别
protected继承:在派生类中,基类public和protected成员变为protected访问级别
private继承:基类所有成员在派生类中变为private访问级别
其实,可以这样理解这个过程:派生列表尝试指定基类所有成员的访问级别为xxx,但由于不能放松访问级别,故比xxx更紧的访问级别保持不变
派生列表访问级别是用以控制派生类的用户(包括派生类对象,派生类的子类等)对基类成员的访问权限的,但不改变本派生类对直接基类成员的访问权限。如,private派生的子类仍能访问基类public和protected成员,但该派生类的派生类或该派生类对象无法访问基类所有成员。
事实上,public继承最为常见
-----------------------------------------------------------------------
struct 和 class有两个区别:
struct成员访问控制默认为public,class成员访问控制默认为private
struct派生列表访问控制默认为public,class派生列表访问控制默认为private
----------------------------------------------------------------------------
友元关系不能被继承,包括两层含义:
(1)基类A中的友元函数或类,不对A派生类成员有特殊访问权限;
(2)作为其他类A的友元基类B,B的派生类不对A的成员有特殊访问权限;
----------------------------------------------------------------------------
每个static成员在继承层次中只有一个
----------------------------------------------------------------------------
派生类引用或指针到基类引用或指针的转换可自动完成,但派生类对象到基类对象的转换会调用基类的复制构造函数或赋值函数,这两个函数的参数均为基类引用,该引用是从派生类引用自动转换过来的。
由于派生列表访问控制可能改变基类成员在派生类中的访问权限,故并非所有派生类对象可转换为基类对象,判断是否可以转换,以基类public成员能否访问为基准:
(0)派生类本身及其友元总是可以转换;
(1)public派生,可转换;
(2)protected派生,派生类的后续派生类可转换;
(3)除(0),不可以转换