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

基本上只用于构造函数前

  1. 指定构造函数或转换函数 (C++11起)为显式, 即它不能用于隐式转换复制初始化,推荐尽可能使用
  2. 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 覆盖的是三个步骤中的第一个步骤

  1. p = operator new(sizeof(A))
  2. p2 = static_cast<A>(p)
  3. p2->构造函数()

delete 分两个步骤,Operator delete 覆盖的是第一个步骤

  1. p->析构函数()
  2. 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。

posted @ 2022-06-16 09:28  某某人8265  阅读(52)  评论(0编辑  收藏  举报