c++程序设计
C++
转型函数
class MyClass {
public:
/* 转型函数
函数名为 operator + 目标数据类型
没有参数与返回值
注意加 const
此处为强转为double类型
*/
operator double() const {//将此类型转为其他类型
return static_cast<double>()
};
};
MyClass m;
double a = 1.0 + m;//此处会先去查找有无合适的operator+函数,没有则将变量m强转为double
explicit
基本上只用于构造函数前
- 指定构造函数或转换函数 (C++11起)为显式, 即它不能用于隐式转换和复制初始化,推荐尽可能使用
- explicit 指定符可以与常量表达式一同使用. 函数若且唯若该常量表达式求值为 true 才为显式. (C++20起)
class Fraction {
public:
// non-explicit one argument constructor 可以传入一个或两个参数
//将别的类型转换为此类型
Fraction(int num, int den=1): m_numerator(num), m_denominator(den) {}
/*
explicit Fraction(int num, int den=1): m_numerator(num), m_denominator(den) {}
加上这个关键字后,只有在创建新对象时才能使用这个构造函数。
*/
Fraction operator+(const Fraction& f) {
return Fraction(...);
}
private:
int m_numerator;
int m_denominator;
}
Fraction f(3, 5);
Fraction d = f + 4;
// 无法直接使用operator+,此处会将4强转为Fraction类型,使用 non-explicit one argument constructor,然后再调用operator+
// 当构造函数用explicit修饰后就无法进行隐式转换了,这个初始化也会报错
mutable
mutable关键字是为了突破const关键字的限制,被mutable关键字修饰的成员变量永远处于可变的状态,即使是在被const修饰的成员函数中。
class Widget{
public:
Widget();
~Widget() = default;
int getValue() const;
int getCount() const;
private:
int value;
mutable int count; // 用于统计getValue方法被调用了几次
};
Widget::Widget() : value(1), count(0) { }
int Widget::getValue() const{
count++; // 常成员函数中不许修改变量的值,mutable修饰的例外
return value;
}
int Widget::getCount() const{
return count;
}
int main()
{
Widget w1;
for(int i = 0; i < 5; i++){
w1.getValue();
}
std::cout << w1.getCount() << std::endl;
return 0;
}
pointer-like classes
被设计的像指针的对象,主要思想是重写了 operator*()
operator->()
智能指针
// shared_ptr 简化原代码,对象内有一个真正的指针,指向目标对象。
template<class T>
class shared_ptr {
public:
T& operator*() const {
return *px;
}
T* operator->() const {
return px;
}
shared_ptr(T* p): px(p) {}
private:
T* px; // 指向类型T的对象
long* pn;
// ...
};
struct Foo {
void method() {...}
};
shared_ptr<Foo> sp(new Foo);
Foo f(*sp);
sp->method(); // 等于 `px->method();` sp->通过operator->函数得到px;但箭头符号特殊,任然通过->再次调用函数
迭代器
template <class T>
struct __list_node {
void* prev;
void* next;
T data;
};
// __list_iterator 内部由一个指针,指向链表中的节点,它自身不是链表中的成员
// node(prev, data, next) <=> node(prev, data, next) <=> node(prev, data, next)
template<class T, class Ref, class Ptr>
struct __list_iterator {
typedef __list_iterator<T, Ref, Ptr> self;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
link_type node;
bool operator==(const self& x) const {
return node == x.node;
}
self& operator++() {
node = (link_type)((*node).next);
return *this;
}
self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}
reference operator*() const {
return (*node).data;
}
pointer operator->() const {
return &(operator*());
}
};
*ite // *((ite->node)->date)
ite->method() // ((ite->node)->date)->method()
function-like classes 仿函数
让一个对象使用起来像函数,通过重载 operator()
template <class T>
struct identity : public unary_function<T, T> {
const T& operator() (const T& x) const {
return x;
}
};
identity<int> ide;
ide(1); // 就像调用函数一样调用它,返回值为1
template <class Pair>
struct select1st : public unary_function<Pair, typename pair::first_type> {
const typename Pair::first_type&
operator() (const Pair& x) const {
return x.first;
}
};
Pair<int, int> p(1, 2);
select1st<Pair> select1func = select1st<Pair>();
select1func(p); // 返回Pair对象的第一个值 1
标准库中仿函数都会继承一些奇特的 base classes
// 一个参数
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
// 两个参数
template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
// 这两个类的大小理论上是0,实际实现时可能为1。
成员模板
模板类中的模板函数
template <class T1, class T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {}
pair(const T1& a, const T2& b) : first(a), second(b) {}
template <class U1, class U2>
pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
}
///////////////////////////////////////////////////////////
class Base1{};
class Derived1: public Base1 {};
class Base2{};
class Derived2: public Base2 {};
pair<Derived1, Derived2> p;
pair<Base1, Base2> p2(p);
// 等于
pair<Base1, Base2> p2(pair<Derived1, Derived2>());
specialization 模板特化
template <class Key> // 泛化的模板。使用任意类型都会使用这段代码
struct hash {
void fun() {
std::cout << "this is generalization." << std::endl;
}
};
// 当模板的参数为char时使用这个特殊处理方案
template <>
struct hash<char> { // 特化的模板,针对特殊情况特殊处理
void fun() {
std::cout << "this is specialization." << std::endl;
}
};
hash<int> a;
a.fun(); // this is generalization.
hash<char> b;
b.fun(); // this is specialization.
模板的偏特化
个数的偏
template <template T, typename Alloc=...>
class vector {
};
template <typename Alloc=...> // 只能从左向右绑定
class vector<bool, Alloc> { // 绑定一个特殊类型
};
范围的偏
template <template T>
class C {
};
C<string> obj1;
template <template S>
class C<S*> { // 当使用者用的是指针,则使用这套代码,指针指向T类型
};
C<string*> obj2;
模板模板参数
template <typename T, template <typename T> class Container>
class XCls {
private:
Container<T> c;
public:
...
};
/*错误,因为容器的模板参数一般不止一个,但因为有默认值,所以一般情况下不写也无妨*/
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> p;
XCLs<long, auto_ptr> p;
// 下方两种用法错误,这两种智能指针不支持
XCLs<double, unique_ptr> p;
XCLs<int, weak_ptr> p;
// 这不是模板模板参数
template <class T, class Sequence = deque<T>>
class stack {
};
stack<int> s1;
stack<int, list<int>> s2; // 此处在传参时需要指定list对象的模板参数
variadic templates 数量不定的模板参数
print() {} // 声明此函数,用于没有参数传入 print(const T& firstArg, const Types&... args) 时
template <typename T, typename... Types> // 不定参数
void print(const T& firstArg, const Types&... args) {
cout << firstArg << end;
print(args...);
// sizeof...(args) 用于获得参数个数的关键字
}
print(7.5, "hello", bitset<16>(16)); // 输出为 7.5hello0000000000010000
// 其中第一个参数作为firstArg,后两个打包到args中,然后调用
print("hello", bitset<16>(16));
// 输出hello,再调用
print(bitset<16>(16));
// 输出bitset,且没有参数打包到args。同时会再调用
print();
关键字 sizeof...()
用于获得不定模板参数的长度。
Operator new
Operator delete
Operator new[]
Operator delete[]
重载
new 关键字可分为三个步骤,Operator new 覆盖的是三个步骤中的第一个步骤
- p = operator new(sizeof(A))
- p2 = static_cast<A>(p)
- p2->构造函数()
delete 分两个步骤,Operator delete 覆盖的是第一个步骤
- p->析构函数()
- operator delete(p)
可以分别重载成员的与局部的new和delete操作
inline void* perator new(size_t size) {
return myAlloc(size);
}
// 此处size由编译器传入,大小为 num * type_size + num所占的空间(为os的字长)
// int* p = new int[5]; 数组大小为 sizeof(int) * 5 + 4 = 24(32位机器下)
inline void* perator new[](size_t size) {
return myAlloc(size);
}
inline void perator delete(void* ptr[, size_t]) {
return myAlloc(ptr);
}
inline void perator delete[](void* ptr[, size_t]) {
return myAlloc(ptr);
}
// 此处调用的是 全局new 与 全局delete 的方式,同时也可以重写
Foo* p = ::new Foo();
::delete p;
Foo* p = ::new Foo[5];
::delete[] p;
重载 operator new()
new 操作可以拥有多个版本,前提是每个版本的声明有独特的参数列,第一个参数必须是size_t,其余参数通过new后的参数填充。
class Foo {
public:
Foo() {}
Foo(int) { cout << "Foo::Foo(int)" << endl; throw Bad(); }
void* operator new(size_t size){ return malloc(size); }
void* operator new(size_t size, void* start) { return start; }
void* operator new(size_t size, long extra) { return malloc(size + extra); }
void* operator new(size_t size, long extra, char init) { return malloc(size + extra); }
// 对应第1个,当第一个抛出错误时调用这个delete函数
void operator delete(void*, size_t)
{ cout << "operator delete(void*, size_t)" << endl; }
// 对应第2个
void operator delete(void*, size_t, void*)
{ cout << "operator delete(void*, size_t)" << endl; }
// 对应第3个
void operator delete(void*, size_t, long)
{ cout << "operator delete(void*, size_t)" << endl; }
// 对应第4个
void operator delete(void*, size_t, long, char)
{ cout << "operator delete(void*, size_t)" << endl; }
};
Foo* pf = new(300, 'c') Foo;
重载 operator delete()
可以拥有多个版本的 class member operator delete()
,但是都不会被delete调用。只有当参数列表相同的new调用的ctor抛出exception时才会调用这些重载版本的 operator delete()
。它只有这一种调用情况,主要用于归还未能完全创建成功的object占用的memory。
但并非所有版本的编译器都是如此。有些不会调用重载的 operator delete。