2-转换函数(把这种东西转换为别人的东西)
转换函数不需要返回类型,就是函数名的类型
#include<iostream>
using namespace std;
class Fraction
{
public:
Fraction(int num, int den = 1)
:m_numerator(num), m_denominator(den){}
//conversion function转换函数
operator double() const
{
return double(m_numerator) / double(m_denominator);
}
private:
int m_numerator; //分子
int m_denominator; //分母
};
int main()
{
Fraction f(3,5);
//编译器会试图找一个函数让这句语句通过
//(1)有没有定义全局的+函数,第一个参数是整数(浮点),第二个参数是fraction
//(2)看能不能把f转为double?看看有没有这种转换函数
double d = 4 + f; //调用operator double() 将f转为double
cout<<d<<endl;
return 0;
}
3-non-explicit one argument constructor(别人的东西转换为这种东西)
two parameter ! one argument ! 可以只有一个输入
one argument -> 只要1个实参就够了!
class Fraction
{
public:
Fraction(int num, int den = 1):m_numerator(num), m_denominator(den){}
//定义函数供显示转换
Fraction operator+ (const Fraction& f);
void print();
private:
int m_numerator; //分子
int m_denominator; //分母
};
inline Fraction Fraction::operator+(const Fraction& f)
{
return Fraction(m_numerator*f.m_denominator + f.m_numerator * m_denominator, m_denominator * f.m_denominator);
}
inline void Fraction::print()
{
cout <<m_numerator<<" "<<m_denominator<<endl;
}
int main()
{
Fraction f(3,5);
Fraction d2 = f+3;
d2.print();
return 0;
}
注意转换函数和隐式转换的构造函数不能同时存在
(一个转成double了,另一个又转成Fraction了)
想要同时存在?把构造函数改为explicit的
4-pointer-like classes(像指针的类)
想要比指针再多一些事情 -> 智能指针
模拟shared_ptr的写法
什么样的操作符用在指针身上呢?这里我们关注这两个操作符:*、->
总结 :智能指针里带一根一般的指针,且里面带一个、->函数*
另外需要注意的是: -> 是会连续的。例如sp->method()、sp->会返回指针px,然后px后面会再紧跟出一个->去调用它的函数method
template<class T>
class my_shared_ptr
{
public:
my_shared_ptr(T* ptr):px(ptr){}
T& operator*() const
{
return *px;
}
T* operator->() const
{
return px;
}
private:
T* px;
};
struct Foo
{
void method()
{
cout<<"hello"<<endl;
}
};
int main()
{
//new 返回的就是指针
my_shared_ptr<Foo> sp(new Foo());
Foo f(*sp);
sp->method();
//等价于px->method
return 0;
}
迭代器:也是一种智能指针,但是不同的是,它还需要处理 ++、-- 因为它要遍历容器
链表(双向)迭代器:
这里是 pointer-like classes关于迭代器 迭代器的(*、->的写法就有变化了)
operator*() 返回了Foo object,再去取它的地址 以调用里面的method
5-function-like classes
Pair是暗示、暗示这里要放Pair
标准库里有很多仿函数,他们里面都是重载了()
9-member template
模版里面还套了一个模版、外面的模版确定了之后,里面的再确定
U1 、U2 构造我 T1 和 T2 的时候,必须满足赋值动作
Derived1 是 Base1的子类
Base1* ptr = new Derived1; -> up-cast
template<typename _Tp>
class shared_ptr:public__shared_ptr<_Tp>
{
...
template<typename _Tp1>
explicit shared_ptr(_Tp1* __p):__shared_ptr<_Tp>(__p){}
}
//智能指针
shared_ptr<Base1> sptr(new Derived1); //模拟up-cast
10-模版特化
先举例泛化
template <class Key>
struct hash {};
特化
//已经被绑定了,所以里面的东西不在了
template<>
//在这里指定使用用charctor
struct hash<char>{
size_t operator() (char x) const {return x;}
};
template<>
struct hash<int>{
size_t operator() (int x) const {return x;}
};
template<>
struct hash<long>{
size_t operator() (long x) const {return x;}
};
//调用:
// hash<long>()这部分是临时对象,后面的(1000)是调用函数
cout << hash<long>() (1000)<<endl;
11-偏特化
(无论怎么偏,要先把原来泛化的模版写出来)
template<typename T, typename Alloc>
class vector
{
...
}
个数上的偏
template<typename Alloc=...>
class vector<bool, Alloc>
{
...
}
范围上的偏(从任意类型,变成了指针类型,范围缩小了,至于它指向什么 都可以)
template<typename T>
class C<T*>
{
...
};
后面再决定,如果你是指针了/bool了,ok用这套偏特化的代码、不是指针就用普通的
12-模版模版参数
template**<>**里typename 和 class共通
//模版模版参数放的是容器
template<typename T,
template<typename T>
class Container
>
class XCls
{
private:
Container<T> c;
public:
...
}
//list自己还未定呢,自己还是个模版
//但是下面这个是错的
//容器有的有第二/第三模版参数,这里过不了 很不幸运 容器需要好几个模版参数,所以过不了
XCls<string, list> mylst1;
应该这样:
template<typename T>
using Lst = List<T, allocator<T>>;
Xcls<string, Lst> mylst2;
很不幸运 容器需要好几个模版参数,所以过不了
这里使用智能指针
//模版模版参数放的是容器
template<typename T,
template<typename T>
class SmartPtr
>
class XCls
{
private:
SmartPtr<T> sp;
public:
XCls() : sp(new T) {};
}
XCls<string, shared_ptr> p1;
这不是模版模版参数
//这里
XCls<string, list> mylst1;
//我们只是传了一个list进去,它没有绑定任何东西,它仍然是一个灰色的模糊的
上面那个呢,要么不传,要么就得全部传进去,所以它不是模版模版参数
13-三个主题
数量不定的模版参数
void print()
{}
//一个和一包
template <typename T, typename... Types>
void print(const T& firstArg, const Types&... grgs)
{
cout<<firstArg<<endl;
print(args...);
}
//当args参数只有0个了的时候,就会调上面的print()
auto
list<string> c;
list<string>::iterator ite;
ite = find(c.begin(), c.end(), target);
变为
list<string> c;
auto ite = find(c.begin(), c.end(), target);
但是不可以下面这样
auto ite;
ite = find(c.begin(), c.end(), target);
ranged-base for
//pass by value
for(auto elem : vec)
{
cout << elem <<endl;
}
//pass by reference
for(auto& elem : vec)
{
elem *= 3;
}
15-Reference
一般都说:
指针指向xxx
引用代表xxx
(引用的底部实现其实也是一个指针指向别人)
注意系统制造出的假象:
1. sizeof(x) == sizeof®
2. &x == &r (他们的地址是一样的)
以下被视为"same signature" 不能同时存在
double imag(const double im){...}
double imag(const double& im){...}
//相同的话会二义性 Ambiguity
//?函数最后加const算不算函数签名的一部分?
//答案是是
const 被视作签名的一部分、可以同时存在
**
double imag(const double im) const{...}
double imag(const double& im) {...}
17-关于vptr和vtable
函数是代码,占内存的一块
静态绑定: call xxxx 运行完再返回来
它实际上运行的:
他根据虚函数的名称以及当时定义的顺序,编译器就知道它当时是把虚函数安排在了第几个
(*(p->vptr)[n])(p);
//或
(* p->vptr[n])(p);
动态绑定三个条件:
- 通过指针调用
- 指针是up-cast
- 调用的是虚函数
就可以把函数编译成上面那个样子
多态、虚函数、动态绑定、说的都是同一件事
//这里不是指针调用的 是强行转换的,所以是静态绑定
B b;
A a = (A) b;
a.vfunc1();
//动态绑定
//(1)
A* pa = new B;
pa->vfunc1();
//(2)
pa =
call 后面不是一个固定地址了
18-关于this
Template method
这里this指的是子类的对象,所以它就符合向上转型的条件
this -> Serialize()
//等价于
(*(this -> vptr)[n])(p);
19- 关于Dynamic Binding
const function 这里可以被 String调用,也可以被const String调用,那这会又冲突
当成员函数const和non-const同时存在,const object 调用 const 版本、non-const object调用non-const版本
20-重载 new 和 delete
counter -> 让编译器知道调用5次构造、5次析构
new[] 的时候,还会另外分配4个字节做counter去记录分配了几个块
构造的时候向下,析构的时候向上
23-new delete 重载
第一参数必须是size_t
new内部被分成了三步
Complex* pc;
void* mem = operator new( sizeof(Complex) ); //分配内存 operator new里面用的malloc
pc = static_cast<Complex*>(mem); //转型(malloc分配出的是void)
pc->Complex::Complex(1,2); //构造函数
delete 内部被分成 两步
String::~String(ps);
operator delete(ps);
注意区分 operator new operator delete 和 new delete 是不一样的,前面的是要带小括号使用的,后面是new表达式
24-Basic_String使用new(extra)
重载了operator new (带小括号的new)
这是class里带的class
当它要create它自己的时候,是要分配一个Rep,再加一个ectra(字符串放这里面)
里面的Rep就是做reference counting 引用的计数 看有多少人和我共享
什么时候要定义placement new呢?当想像这样 无声无息 不知不觉地分配一些空间的时候 可以这样借鉴
重载placement new 为的是里面有一个reference counting的设计