侯捷C++高级面向对象编程_下_课程笔记

friend(友元):相同Class的各个objects互为friends(友元)

class complex{

public:

       complex (double r = 0, double I = 0) : re (r), im (i) { }

       //一个对象的成员函数可以调用另一个对象的私有成员变量,全因为他们是同一个类,默认为友元

       int func(const complex& param){

       return param.re + param.im;

}

private:

       double re, im;

};

 

array new 一定要搭配 array delete

delete的两步删除操作执行的第一步是析构,第二步是free()

若array new 没有array delete的话,析构并不会将数组中所有的变量析构,会造成new数组中变量的内存泄露

 

Conversion function,类型转换函数

将一种类的对象的类型转换成其他类型的函数

class Fraction

{

public:

    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den){}

    operator double() const {    //类型转换

        return (double) (m_numerator / m_denominator);

    }

private:

    int m_numerator;        //分子

    int m_denominator;      //分母

}

调用举例:

Fraction f (3, 5);

double d = 4 + f;  //调用operator将f转为0.6

 

第二种Non-explicit-one-argument ctor,无explicit且只需一个实参的构造函数

class Fraction

{

public:

    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 d2 = f + 4;      //会首先调用non-explicit ctor将4转换成Fraction类型

                                   //然后再调用operator+

 

问题:当出现多条可选方案进行类型转换的时候编译器会报错产生歧义,例如在同时拥有类型转换和类型+的时候,double d = f + 4; //可f转double,也可以4转F再转double

 

第三种explicit-one-argument ctor(明确的只需一个实参的构造函数)

class Fraction

{

public:

    explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) { }

    operator double() const{

        return (double) (m_numerator / m_denominator);

    }

    Fraction operator+(const Fraction& f) {

        return Fraction(......);

    }

private:

    int m_numerator;        //分子

    int m_denominator;      //分母

}

调用举例:

Fraction f (3 ,5);

Fraction d2 = f + 4;     //Error:编译器未能将double转为Fraction

//加上explicit之后的构造函数只能在构造的时候调用

 

pointer-like classes(关于智能指针):

template<class T>

class shared_ptr

{

    T& operator*() const { return *px; }

 

    T* operator->() const { return px; }

 

    shared_ptr(T* p) : px(p) { }

 

private:

    T* px;

    long* pn;

...

}

调用举例:

struct Foo

{

    ...

    void method(void){}

}

 

shared_ptr<Foo> sp(new Foo);

 

Foo f(*sp);     //指针的解引用实现,基于Operator*实现

 

sp->method();   //指针指向变量的内部函数的调用,等同于px->method

//特别的有,->运算的特别:箭头作用不会调用一次就停止,会继续作用下去,所以sp->等于px->

 

pointer-like classes(关于迭代器):

不同于一般的智能指针,迭代器还需要它所特有的功能++\--,且他的操作符重载亦有不同

 

T& operator*() const

{ return (*node).data; }

 

T* operator->() const

{ return &(operator*()); }

调用举例:

 

T& operator*() const

{ return (*node).data; }

 

T* operator->() const

{ return &(operator*()); }  //返回这个对象的地址

 

list<Foo>::iterator ite;

...

*ite;   //获取一个Foo object

ite->method();

//意思是调用Foo::method();

//相当于(*ite).method();

//相当于(&(*ite))->method();

 

Function-like classes,所谓仿函数:即通过()调用的类

template <class Pair>

struct select1st {

    const typename Pair::first_type&

    operator() (const Pair& x) const

    { return x.first; }

}

调用举例:

select1st<pair>()(x);      //第一个()是生成对象,第二个()是调用operator()成员函数

 

member template成员模板·:

template<class T1, class T2>

struct pair {

    ...

    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) {}

}

调用举例:

例如在pair类中,我们可用通过调用任意的满足模板类型的U1、U2来构造对象,只要他们能满足其自身能转换成T1、T2

具体表现在U1、U2为子类,T1、T2为父类时,可用转换

同样也可以表现在其他可以进行类型转换的场景如:

pair<double,double> p({1,1});     //整数转小数

 

specialization,模板特化:

泛化的反义词,根据特殊的类型进行特殊的处理

template <class Key>

struct hash {};

 

template <>

struct hash<char> {

    size_t operator() (char x) const { return x; }

};

 

template <>

struct hash<int> {

    size_t operator() (int x) const { return x; }

};

调用举例:

cout << hash<int>() (1000);        //第一个()产生一个临时对象,第一个括号调用成员函数

 

partial specialization,模板偏特化——个数的偏:只将有限个数的参数绑定为特定的类型

template<typename T, typename Alloc=...>

class vector

{

    ...

};

 

template<typename Alloc=...>

class vector<bool, Alloc>

{

    ...

}

——范围的偏:将任意类型缩小到任意类型的指针

template <typename T>

class C

{

    ...

};

 

template <typename U>

class C<U*>

{

    ...

};

 

template template parameter, 模板模板参数

template <typename T,

        template <template T>

            class Container

        >

class XCls

{

private:

    Container<T> c;

...

};

 

template<typename T>

using Lst = list<T, allocator<T>>;

调用举例:

XCls<string, Lst> mylst;

解释:

在定义模板时定义了模板中的模板,使得程序更加泛化

特殊的有:

当第一个模板会限制第二个模板时泛化会被限制,故其不能称为模板模板参数,且写法不同

template <class T, class Sequence = deque<T>>

class stack {

...

protected:

    Sequence c; //底层容器

};

调用举例:

stack<int, list<int>> s;

 

variadic templates:数量不定的模板参数

void print()

{  

}

 

template <typename T, typename... Type>

void print(const T& firstArg, const Types&...args)

{

    cout<<firstArg<<endl;

    print(args...);     //递归处理

}

调用举例:

print(7.5, “hello”, bitset<160>(377),42);      //实际调用四次,当最后print传入为空时调用重载

特殊的有:

当要获取args中参数个数时使用

sizeof…(args);

 

reference:引用

编译器的假象(好的假象):当r是x的引用时

sizeof(r)  == sizeof(x);        //实际引用只占4字节

&r == &x;

常见用途:

void func1(Cls obj) { ob.xxx(); }

void func2(Cls& obj) { ob.xxx(); }  //被调用端用法也相同

void func3(Cls* obj) { ob->xxx(); }

...

Cls obj;

func1 (obj);

func2 (obj);    //接口相同,方便调用

func3 (&obj);   //接口不同,会造成困扰

特别的有:

reference通常不用于声明变量,而用于参数类型(parameters type)和返回类型(return type)的描述。

以下被视为“same signature”(所以二者不能同时存在):

double imag (const double& im) { … };

double imag (const double  im) { … };      //Ambiguity,但是可以在()后加const加以区分

 

Inheritance(继承)关系下的构造和析构:

构造由内而外:子类的构造函数先调用父类的default构造函数,然后才执行自己

Derived::Derived(…) : Base() {…};  //构造时编译器自动调用父类进行构造

Derived::~Derived(…) { … ~Base() };    //析构时自动添加父类析构

Composition(复合)关系下的构造和析构:

同样的构造由内而外,析构由外而内

构造时先构造内部包含的类,析构时先析构外部的容器类

 

对象模型(Object Model) : 关于vptr和vtbl (虚指针和虚表)

对于非虚成员函数其在类中独立创建,对于虚函数,其使用虚表和虚指针来实现,虚表中包含该类的所有虚函数地址,子类虚表包含一切父类的虚函数地址,但如果对虚函数进行了重载则会让虚表中该函数的虚指针指向它重新创建的虚函数。

 

根据c的语法调用虚函数即(* (p->vptr)[n] ) (p) 或  (* p->vptr[n] ) (p);

其在具体的子类调用时为(*(this->vptr)[n])(this);

对象模型(Object Model) : 关于Dynamic Binding

静态绑定时,函数调用时call直接通过获取静态函数地址来调用

动态绑定时,汇编代码call通过虚指针vptr来获取函数地址以实现函数动态绑定

 

关于面向对象中的const:

const object不可以调用non-const成员函数

const成员函数不可以调用non-const成员函数

实际举例:

Class template std::basic_string<…>有如下两个成员函数:

charT

operator[] (size_type pos) const

{ ....../* 不必考虑COW*/ }

 

reference

operator[] (size_type pos)

{ ....../*必须考虑COW*/ } //COW即 copy on write

当多个对象共用一个字符串时,如果某个对象想要改变该字符串,即它需要考虑COW,即使用下面那个成员函数,如果不存在改变的情况则两个都可以使用

但!,C++又规定有:当成员函数的const和non-const版本同时存在时,const object只会(只能)调用const版本,non-const object只会(只能)调用non-const版本

 

关于operator new和operator delete

new由三步组成:operator new(底层调用malloc获取void指针指向的要求大小空间)、static_cast<type>(对上一步malloc申请的void指针做强制类型转换)、构造函数调用    

delete由两步组成:析构函数、operator delete (底层调用free)

new\delete虽然不可以重载,但其中operator new\delete可以重载,既可以全局重载也可以成员函数重载(一般用来写内存池)

例如:对单个对象进行new/delete

class F00 {

public:

    void* operator new(size_t);

    void  operator delete(void*, size_t);

    //delete的size_t参数可选填...

}

对数组对象(per-class allocator)进行new/delete

class F00 {

public:

    void* operator new[](size_t);

    void  operator delete[](void*, size_t);

    //...

}

调用时:

Foo* p = new Foo[N];

等于

       try{

       void* men = operator new(sizeof(Foo) * N);

       p = static_cast<Foo*>(men);

       p->Foo::Foo();      //N次

}

dwelete []p;

等于

       p->~Foo();    //N次

       operator delete(p);

如果没有重载operator new\delete或Foo* pf = ::new Foo; / ::delete pf;

就会调用就调用globals:void* ::operator new(size_t); / void ::operator delete(void*)

 

我们可以重载多个版本的class member operator new(),前提是每个版本都要有其独一无二的参数列表,其中第一个参数必须是size_t,其余参数根据需求添加。

同样我们可以重载多个版本的operator delete(),但他们绝不会被delete调用。只有当new所调用的ctor使用之前定义的对应版本的operator new()时抛出异常,才会调用这些重载版本的operator delete(),主要用来归还未能完全创建成功的对象所占用的内存。

即使operator delete(…)未能一一对应于operator new(…),也不会出现报错,系统默认你放弃处理ctor发出的异常

 

posted @   七星易  阅读(45)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示