侯捷cpp-oop(下) 笔记

C++程序设计兼谈对象模型-导读

conversion function, 转换函数
class Fraction {
public:
Fraction(int num, int den=1) : m_numerator(num), m_denominator(den){};
operator double() const {
return 1.0 * m_numerator / m_denominator;
}
private:
int m_numerator;
int m_denominator;
};
转换函数没有返回值
函数名为
operator doubleno parameters
转换这个语义本身不改变成员变量,因此需要加
const修饰。虽然不加const也可以通过编译。
non-explicit-one-argument ctor
Fraction(int num, int den=1)这个ctor,是2 parameters, 1 argument. 只需要一个 argument(实参) 就可以实例化。
class Fraction {
public:
Fraction(int num, int den=1) : m_numerator(num), m_denominator(den){};
Fraction operator+ (const Fraction& f) {
return Fraction(0, 0); // ignore reality.
}
private:
int m_numerator;
int m_denominator;
};
int main() {
Fraction f(3, 5);
Fraction d2 = f + 4;
return 0;
}
f + 4, 会查找到 Fraction operator+ (const Fraction& f) 这个函数.
然后会尝试调用non-explict ctor把 4 转化为 Fraction 类型, 再传递进入 operator+ .
explict
explict 会告诉编译器不要进行隐式转换, 可以用来修饰ctor和conversion function. 如果需要进行转换,需要在调用处显式指出。
修饰 conversion function 这一点大部分资料不会指出,但其实是可行的. 可以参考 user-defined conversion function .
pointer-like classes
关于智能指针
一般pointer能做的事情,pointer-like classes 也可以做。因此能应用到 pointer 上的运算符,也可以应用到 pointer-like classes, 如operator*()、 opreator->().

对于 sp->method(), 操作符 -> 会作用在 sp 上, 调用 T* shared_ptr::operator->()const.
调用后,如果 -> 是一般的操作符,如*, 会被消耗, 变为 px method().
但是 -> 有一个特性,会在有操作数存在的情况下,一直存在,因此经过一次调用后不会被消耗,仍然是 px->method().
关于迭代器


迭代器也是一类pointer-like class, 使用者调用 * 意图获得 data 本身; 使用者调用 ->, 意图获得 data 的地址,并且在改地址上继续复用 -> 来进行一系列操作。
function-like classes, 所谓仿函数
operator() 也叫 function call operator.
只要 class 内部重载了 operator() 那么就可以称为 functor(仿函数).
标准库中仿函数的奇特模样


unary_function 和 binary_function 既没有成员变量,也没有成员函数,理论上实际的大小是 0.
至于为什么要有 c++ 基本概念之 functor 这篇文章讲的不错。
member function, 成员模版

这样做可以让ctor更有 "弹性", 可以接收多种类型的参数。

specialization, 模版特化
泛化和特化互为相反。
#include <iostream>
template <typename Key>
class hash {};
template<>
class hash<char> { // 格式类似于这种, 把上面的不确定的 typename 拿下来, 变为确定的类型.
public:
size_t operator() (char ch) {
return size_t(ch);
}
};
template<>
class hash<long> {
public:
size_t operator() (long l) {
return size_t(l);
}
};
int main() {
hash<long>()(1l);
hash<char>()('a'); // 第一个() 构造出一个匿名对象, 第二个() 调用重载后的小括号运算符.
return 0;
}
特化 也叫 全特化(full specialization), 也有 partial specialization.
partial specialization 模版偏特化
函数模版只能全特化, 而类模版可以偏特化和全特化。
特化必须在同一个命名空间下进行,但 std 是一个例外,用户可以特化其中的模版,但不可以添加模版。
个数的偏
偏特化的顺序只能从左向右绑定模版参数.
template <typename T1, typename T2>
struct PPair {
int hash() {
std::cout << "in general hash() " << std::endl;
}
};
template <typename T2>
struct PPair<int, T2> {
int hash() {
std::cout << "in <int, T2> partial hash()" << std::endl;
}
};
int main() {
PPair<double, float>().hash();
PPair<int, double>().hash();
return 0;
}
in general hash()
in <int, T2> partial hash()
范围的偏

template<typename T>
struct JoJo {
void stand() {
std::cout << "in general stand()" << std::endl;
}
};
template<typename U>
struct JoJo<U*> {
void stand() {
std::cout << "in U* type stand()" << std::endl;
}
};
int main() {
JoJo<double>().stand();
JoJo<double*>().stand();
JoJo<int*>().stand();
return 0;
}
in general stand()
in U* type stand()
in U* type stand()
模版模版参数 template template parameter



关于 C++ 标准库

查看 C++ 版本
std::cout << __cplusplus << std::endl;
三个主题
variadic templatem, 数量不定的模版参数
关键字 typename... 可以使用 sizeof...(args) 来获取剩余的参数数量。

void print() {}
template<typename T, typename... TS>
void print(const T& firstArg, const TS&... args) {
cout << firstArg << "\t rest number of TS is " << sizeof...(args) << endl;
print(args...);
}
int main() {
print(1.2, 1, "hello");
return 0;
}
1.2 rest number of TS is 2
1 rest number of TS is 1
hello rest number of TS is 0
将函数的参数分为 一个 和 一包,递归处理第一个,一包 的数量可以为 0, 最后递归调用到 void print().
auto

range-base for
支持两种遍历, pass by value 和 pass by reference.

Reference

reference 的常见用途
“reference是一种漂亮的pointer”

reference 通常不用于声明变量,而用于 参数类型(paramters type) 和 返回类型(return type) 的描述。
const也是函数签名的一部分
// 这两个函数的签名不同
void m_func(const int&) {}
void m_func(const int&) const {}
复合&继承关系下的构造和析构

这里的结果和“面向对象(上)”里实验的结果一致。
关于 vptr 和 vtbl


范型通过指针实现,是因为指针的内存大小都相同。 而在继承体系中,不同类的占用内存大小不一定相同。
动态绑定(多态、虚机制)的三个条件:
指针调用
指针向上转型 (
up-case)调用的是
virtual function
关于 this

关于动态绑定

这里通过对象调用,而非通过指针,因此这里是 static binding, 调用了父类的函数,而非 dynamic binding.

谈谈 const

函数重载 不必考虑函数的返回值,只考虑函数签名(非返回值的部分)。
char ch = s[5]; 和 s[5] = 'a'; 分别调用 const函数 和 非const函数.
常量字符串 只调用 const函数, 不必考虑 COW.
关于 new, delete
new Obj() 是 new expression, 会调用 operator new() 来进行内存分配.
重载 operator new, operator delete
重载 ::operator new, ::operator delete, ::operator new[], ::operator delete[]

这些函数由编译器调用。
重载 member operator new/delete

class Foo {
public:
void* operator new(size_t t) {
std::cout << "in overloading operator new()" << std::endl;
return ::operator new(t);
}
void operator delete(void* ptr, size_t t) { // the second parameter size_t is optional
std::cout << "in overloading operator delete(void*, size_t)" << std::endl;
::operator delete(ptr, t);
}
void operator delete(void* ptr) {
std::cout << "in overloading operator delete(void*)" << std::endl;
::operator delete(ptr);
}
};
int main() {
Foo* p = new Foo();
delete p;
}
in overloading operator new()
in overloading operator delete(void*)
重载 member operator new[]/delete[]

示例
new/delete operator
new, new[] 完成三件事情
调用 malloc 分配内存(这一步由
::operator::new(size_t)完成),再调用构造函数创建对象,
A::A()返回指针
delete, delete[] 完成两件事 调用析构函数(~A()),再释放内存(operator delete(a))
可以通过 ::new , ::delete 来绕过 member operator new/delete, 来使用全局默认的 new & delete


重载 new(), delete() -- placement new/delete ()



Basic_String 使用 new(extra) 扩充申请量




