侯捷C++高级编程(下)

文章详细探讨了C++中的转换函数(如operatordouble),显式构造函数,友元函数,智能指针(如shared_ptr),仿函数(function-likeclasses),以及模板、引用、内存管理(new,delete,placementnew)等概念,还涉及了继承与组合对象模型、const规则和异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

21

主题1:转换函数

转换函数

  • 转换函数是将本类对象转化为别的东西
/*
 * 1. 转换函数没有返回类型
 * 2. 转换函数一般需要加上const
 */
class Fraction
{
public:
     Fraction(int num,int den=1):m(num),n(den)
     {
         cout<<"Fraction(int num,int den=1): m/n = "<< m/n<<endl;
     }
     operator double()const
     {
         cout<<"调用转换函数double():"<<(double)( m/n )<<endl;
         return (double)( m/n );
     }
private:
    int m;
    int n;
};
int main(int argc, char** argv)
{
    Fraction f(8,2);
    double d = 5 + f;
    /*
     * 首先 4
     * */
//    Fraction d = f + 4;

    cout<<d<<endl;
    /*
     * 输出结果:
     * Fraction(int num,int den=1):m/n = 4
     * 调用转换函数double()
     * 8
     * 分析:f(8,2): 首先调用构造函数
     * 然后;5 + f 中的f调用转换函数转换为double
     */
}

non-explict-one-argument ctor

  • 将别的东西转化为本类
class Fraction
{
public:
// 下面的构造函数称其为non-explict-one-argument ctor 
    Fraction(int num,int den=1):m(num),n(den)
    {
        cout<<"Fraction(int num,int den=1): m/n = "<< m/n<<endl;
    }

    Fraction operator+(const Fraction &f)
    {
        cout<<"调用operator+()函数"<<endl;
        return Fraction(m+f.m,n+f.n);
    }
private:
   int m;
   int n;
};
int main(int argc, char** argv)
{
   Fraction f(8,2);
   Fraction d1 = f+5;// === f.operator+(5);
   /*
    * 首先f+5中的5调用构造函数Fraction(int num,int den=1)
    * 然后调用operator+()函数
    */
}

注意:提供默认值的构造函数可以将一种类中的为提供默认值的属性类型转换为该类类型---即将一种类型转化为本类类型 operaor double() --- 则是将本类型转化为double类型,两者转换方向刚好相反;但是我们一般所说的转换函数是后一种类型;
ambiguous error

class Fraction
{
public:
     Fraction(int num,int den=1):m(num),n(den)
     {
         cout<<"Fraction(int num,int den=1): m/n = "<< m/n<<endl;
     }
     operator double()const
     {
         cout<<"调用转换函数double():"<<(double)( m/n )<<endl;
         return (double)( m/n );
     }

     Fraction operator+(const Fraction &f)
     {
         cout<<"调用operator+()函数"<<endl;
         return Fraction(m+f.m,n+f.n);
     }
private:
    int m;
    int n;
};
Fraction operator+(int a,const Fraction &b)   
{
//解决5+f的问题
}
int main(int argc, char** argv)
{
    Fraction f(8,2);
    Fraction d1 = f+5;// Use of overloaded operator '+' is ambiguous (with operand types 'Fraction' and 'int')
    Fraction d1 = 5+f;
}

explict的使用:一般用在构造函数前面,表示此构造函数只能用于构造不能用于其他目的
explicit Fraction(int num,int den=1):m(num),n(den)
Fraction d1 = f+5;// error
但是仍然会出现:error: conversion from ‘double’ to non-scalar type ‘Fraction’ requested
解释:
f+5中首先由于在构造函数前面加上了explict所以不会ambiguous,f首先转换为double记为a,然后执行a+5后产生一个double记为c,但是c不能转化为Fraction,因为explict

:
Fraction operator+(int a,const Fraction &b)
{
//解决5+f的问题
}
是否可以成员函数实现,是否是下面的

class Fraction
{
	  friend  Fraction operator+(int a,const Fraction &f)
     {
         ;
     } //注意这不是Fraction的成员函数,这个是全局函数。只不过在Fraction中声明为友元函数之后直接实现了。
}
//这里的5+f的实现利用friend成员函数没有this指针的特点

关于友元函数

  1. 友元函数没有this指针
  2. 因为友元函数是类外的函数,所以它的声明可以放在类的私有段或公有段且没有区别。
  3. 友元函数是不能被继承的,就像父亲的朋友未必是儿子的朋友
    友元函数的分类:
    根据这个函数的来源不同,可以分为三种方法:
    普通函数友元函数:
    目的:使普通函数能够访问类的友元
    语法:
    声明: friend + 普通函数声明
    实现位置:可以在类外或类中
    实现代码:与普通函数相同
    调用:类似普通函数,直接调用
class INTEGER

 {

  friend void Print(const INTEGER& obj);//声明友元函数

 };

void Print(const INTEGER& obj)

{

   //函数体

}

void main()

{

  INTEGER obj;

  Print(obj);//直接调用

}

类Y的所有成员函数都为类X友元函数—友元类
目的:使用单个声明使Y类的所有函数成为类X的友元,它提供一种类之间合作的一种方式,使类Y的对象可以具有类X和类Y的功能。
语法:
声明位置:公有私有均可,常写为私有(把类看成一个变量)
声明: friend + 类名(不是对象哦)

class girl;

class boy

{

public:

  void disp(girl &);

};

void boy::disp(girl &x) //函数disp()为类boy的成员函数,也是类girl的友元函数

{

  cout<<"girl's name is:"<<x.name<<",age:"<<x.age<<endl;//借助友元,在boy的成员函数disp中,借助girl的对象,直接访问girl的私有变量

}

class girl

{

private:

  char *name;

  int age;

  friend boy; //声明类boy是类girl的友元

};

类Y的一个成员函数为类X的友元函数
目的:使类Y的一个成员函数成为类X的友元,具体而言:在类Y的这个成员函数中,借助参数X,可以直接以X的私有变量
语法:
声明位置:声明在公有中 (本身为函数)
声明:friend + 成员函数的声明
调用:先定义Y的对象y—使用y调用自己的成员函数—自己的成员函数中使用了友元机制
代码:
实现代码和2.4.2.3中的实现及其相似只是设置友元的时候变为friend void boy::disp(girl &);

  • 修改一
#include<iostream>
using namespace std;

class Fraction
{
public:
    explicit Fraction(int num, int den = 1):m(num), n(den)
    {
        cout << "Fraction(int num,int den=1): m/n = " << m / n << endl;
    }
    operator double()const
    {
        cout << "调用转换函数double():" << (double)(m / n) << endl;
        return (double)(m / n);
    }

  /*  Fraction operator+(const Fraction& f)
    {
        cout << "调用operator+()函数" << endl;
        return Fraction(m + f.m, n + f.n);
    }
    */
private:
    int m;
    int n;
};

int main(int argc, char** argv)
{
   Fraction f(8, 2);
   double d1 = f + 5;// Use of overloaded operator '+' is ambiguous (with operand types 'Fraction' and 'int')
}

主题2:pointer-like classes 智能指针

pointer-like classes 智能指针

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;
    long *pn;
};
struct FOO
{
    void method();
};
int main(int argc, char** argv)
{
    shared_ptr<FOO> sp(new FOO);
    FOO f(*sp);
    sp->method(); // == px->method(0)  ->会作用之后会继续出现->

    return 0;
}

pointer-like classes 迭代器

template <class T, class Refclass Ptr>
struct __list_iterator { //这是一个链表
    typedef __list_iterator<T, Ref, Ptr> self;
    typedef Ptr pointer;
    typedef Ref pointer;
    typedef __list_node<T>* link_type;
    link_type node;
    bool operator==(const self& x) const { return node == x.node; }
    bool operator==(const self& x) const { return node != x.node; }
    reference operator*() const { return (*node).data; }
    pointer operator-> const { return &(operator*())}
    self& operator++() { node = (link_type)((*node).next); return *this }
    self& operator++(int) { self tmp = *this; ++*this; return tmp; }
    self& operator--() { node = (link_type)((*node).prep); return *this }
    self& operator--(int) { self tmp = *this; --*this; return tmp; }
};

主题3: function-like classes 仿函数

template<class T>
struct identity
{
    const T& operator()(const T& x)const
    {
        return x;
    }
};

主题4:namespace

#include <iostream>
#include<list>

namespace jj01
{
//开始设计
	template<typename T>
	using Lst=list<T,allocator<T>>;
	void test()
}
int main()
{
	jj01::test()
}

主题6: 模板

class template,类模板

Function template 函数模板

  • 函数模板不必指定类型

member template 成员模板

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

pair<Derived1,Derived2> p;
pair<Base1,Base2> p2(p);
等价于pair<Base1,Base2> pw(pair<Derived1,Derived2>());  //用子类初始化父类

specialization 模板特化

template <class Key>
struct hash  {  };
--------------------上面的是泛化-------
template<>  //Key被锁定了
struct hash<char>  {
  size_t operator() (char x)  const  { return x;  }
}template<>  //Key被锁定了
strcut hash<int>  {
  size_t operator() (int x)  const  {return x;  }

template<>  //Key被锁定了
struct hash<long>  {
  size_t operator() (long x)  const  {  return x;  }
};

parti specialization 模板偏特化

个数的偏

在这里插入图片描述
因为上面的模板有两个参数,但是目前我只需一个值绑定一个

范围的偏

在这里插入图片描述

template template parameter 模板模板参数

在这里插入图片描述
XCIS<string,list> mylist1; 第一个确定为string,但是第二个不确定传入模板;但是是错误的;虽然 Container变为 list但是一些容器的模板有一个以上的参数,我们一般只是定义了一个参数,上面不通过是由于语法问题参数不足导致
在这里插入图片描述
打岔:由于unique和weak的一些特性导致错误。
在这里插入图片描述
上面不是模板模板参数,由于list已经指定模板了

variadic templates 数量不定的模板参数

void print()
{
    cout<<"------"<<endl;

}

template<typename T,typename ... Types>
void print(const T& firstArg,const Types&...args)
{//  参数:一个 + 一包
    cout<<firstArg<<endl;
    cout<<sizeof...(args)<<ends;
    print(args...);
}
int main()
{
    print(7.5,"hello",42);
    /*
7.5
2 hello
1 42
0 ------
     这个模板参数可变的函数:参数可以分为一个+一包,这个函数是递归调用
     首先是:print(first=7.5,args=("hello",42)) 然后输出firstArg,然后size...(args)==2
     然后是:print(first="hello",args=(42)),打印出“hello”然后size...(args)==1
     最后是:print(first=42,args=),打印42,然后size...(args)===1调用无参数的print()
     */
    return 0;
}

在这里插入图片描述

C++标准库

在这里插入图片描述

for(int i:{1,2,3,4})
	cout<<i;
//{1,2,3}是个容器

主题7:reference 引用

  1. object和其reference的大小相同,地址也相同(全都是假象)
  2. reference通常不用于声明变量,一般用于函数的参数类型和函数返回类型

主题8:对象模型

继承关系下的构造和析构

  1. 构造应该由内而外
  2. 析构应该由外二内

组合关系下的构造和析构

  1. 构造应该由内而外
  2. 析构应该由外二内

继承+组合 关系下的构造和析构

对象模型:vptr(虚指针)和vtbl(虚表)

  1. 只要一个类含有虚函数,那么该类一定含有虚指针。多一个虚函数就多一个虚指针
  2. 父类有虚函数子类一定有虚函数
    在这里插入图片描述
    函数:
  3. 静态绑定
  4. 动态绑定
    动态绑定的条件
  • 虚函数
  • 向上转型

this

主题9:const

  1. const只能放在成员函数后面
  2. const属于函数签名的一部分在这里插入图片描述

主题10:new delete

  1. operator new 重载不能加inline,也不能在任何一个namespace中
  2. new作用后发生的两个操作(new控制的是第一步)
  • 分配一块内存
  • 在这块内存上构造对象
void operator new(std::size_t size);
/*
new Type(args);  ===  openator new(sizeof(Type));
*/
void operator new(std::size_t size);
/*
new Type[n];  ===  openator new[](sizeof(Type)*n);
*/
    1. operator delete重载不能加inline,也不能在任何一个namespace中
void operator delete(void *ptr) noexpect;
void operator delete[](void *ptr) noexpect;
//noexpect不释放错误

delete发生下面两个步骤:

  • 析构这个对象
  • 释放这块对象
  1. delete重载函数是不欢迎异常的
    new与malloc的区别
  2. 即使size==0,operator new也会分配一些内存,而malloc分配大小为0的内存是未定义的
  3. 分配失败,operator new抛出std::bad_alloc异常,而malloc返回空指针

重写new和delete的情况

  • 检测使用上的错误
  • 为了提高质量
  • 为了收集使用的数据

重载 operator new,operator new[],operator delete,operator delete[]

在这里插入图片描述

在这里插入图片描述
https://zhuanlan.zhihu.com/p/526244459
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

placement new

  1. 定位放置new”(placement new)操作
  2. 定位放置new操作的语法形式不同于普通的new操作。例如,一般都用如下语句A* p=new A;申请空间,而定位放置new操作则使用如下语句A* p=new (ptr)A;申请空间,其中ptr就是程序员指定的内存首地址。
    在这里插入图片描述
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值