C++程序设计(II)兼谈对象模型
摘要
第三讲主要讲解了C++语法上的一些重要的知识,包括对象和其他类型之间的转换、像指针一样的类、像函数一样的类(这样的形容方式在我看来恰到好处,生动形象~~)、各种类型的模板、三个c++11的主题(pack、auto、基于范围的for)、以及对引用的深入剖析。还谈到面向对象编程和泛型编程是c++技术的主流。c++的标准库主要运用这两种编程思想所写。
1. 对象和其他类型之间的转换
1.1 conversion function转换函数——将对象转换为其他类型
转换函数是类中的成员函数,可以转换为任意类型,包括先前自定义的类型。
特点:不用写返回值类型,实际上返回类型是operator后面的类型;没有函数的参数;最好加const。
eg.表示分数的类中,包含分子和分母,要转换为分数。
Fraction::operator double() const
{
return (double)(m_a/m_b);
}
使用方法:
Fraction f(3, 5);
double d = f + 4; (首先编译器会找有没有对加号的重载和有没有对f转换为double类型的重载,这两种方式不能同时存在,找到哪种就采用哪种方法运算,此处就是要将f转换为double,再相加。)
1.2 explicit——隐式的将其他类型转换为对象
还是继续以上面为例,eg. double d = f + 4;
如果有对加号进行重载(两个Fraction对象相加),没有定义double转换函数时,编译器会将4隐式转换为Fraction对象(Fraction类中存在只有一个实参one argument的构造函数没有声明为explicit的情况下),再相加。
当double转换函数和对加号运算符重载都存在的,且只有一个实参的构造函数没有声明为explicit的情况下,编译时会产生歧义ambiguous,因为有两种方案进行double d = f+4,一种是将f转换为double,一种是将4隐式转换为Fraction对象。
2. 两种特殊的类
2.1 pointer-like class
- 智能指针share_ptr:会重载*和->运算符。
- 迭代器:会重载*、->、++、--、==、!=。。。
2.2 function-like class
有重载()运算符的类,那么这种类的对象叫做函数对象(泛函数)。
标准库中的泛函数都会继承于父类,这些父类没有成员函数和数据成员,但有一些typedef.
3. template模板
3.1 class template类模板
template<typename T>
class test
{
};
3.2 function template函数模板
template<typename T> inline void test();
3.3 member template成员模板
标准库中一些构造函数使用成员模板。
Base1 *ptr = new Derived1; //up-cast指针向上转型,利用基类的指针指向派生类对象
3.4 specialization模板特化
特化是泛化的反义词,泛化就是模板,泛化又叫全full特化。特化是对模板的特征化,具体化。有特化和泛化,会优先调用特化。特化可以有多个。
template <typename T>
struct hash { };
template <>
struct hash<long>
{
size_t operator ()(long x) const { return x; }
};
cout << hash<long>()(1000);
其中hash<long>()表示临时对象,(1000)表示调用小括号运算符重载的函数
3.5 partial specialization偏特化
(1)个数的偏:从左到右连续绑定一个或多个typename.
(2)范围的偏:将typename T的T的类型范围缩小,由任意类型缩小为指针。
3.6 template template parameter模板模板参数
template<typename T, template <typename T> class Container> 第二个模板参数也是个模板。使用时并不绑定类型,是模板模板参数。
template<typename T, class Sequence = deque<T> > //第二个模板参数具有默认值,使用时会绑定类型,所以这个不是模板模板参数。
4. 关于c++标准
算法+数据结构=程序
确认编译器是否支持C++11,cout << __cplusplus << endl;
一般返回201103或是199711,或是2014..
5. 三个c++11主题
(1)variadic templates(since c++11) 模板支持不确定模板参数的个数。
template<typename T, typename... Types> ...表示一包pack
void print(const T& firstArg, const Types &args){}
eg. print函数进行递归打印参数,开始时传入一个参数和一包,最后一次传入时,没有参数会调用无参的print函数。
sizeof...(args)得到这个pack中参数的个数。
(2)auto(c++11)
要让编译器能推导出来是什么类型,需要对auto变量定义的同时赋值。
(3)ranged-base for(c++11)
for(变量:容器) {...} 编译器会将容器中每个元素赋值给变量。传值不会对容器中对应的值做更改,传引用容器中才会被修改。与算法中的for_each用同样作用。
{1,4,6,7,9} 这种表示也来自c++11,表示一个容器。
6.引用
特点:引用的底层由指针实现,但逻辑上它的值是所引用变量的值。引用可以赋值为另一个引用。可以通过引用改变对应的变量的值。一个引用不能再去引用其他变量。对象和其引用的大小和地址都相同(假象)。
引用多用于函数传参和作为返回值类型,函数中引用与对象的用法(.)相同,与传入指针用法(->)不同。外面传参数时,引用和对象用法也一致,指针则需要加&。
int &r=x; r和x的sizeof大小和地址都相同(这都是假象)。
像double func(const double x)和double func(const double &x)这两个函数不能同时存在,被认为签名signature是相同的(签名是否相同包含比较函数名和其参数,还有是否是const函数,不包含对返回值类型的比较),如果同时存在这两个函数,都是通过double t; func(t);
这种写法在外部调用的,编译器无法识别是调用的哪个函数。