C++面向对象高级编程(下)

🍉欢迎点赞 👍 收藏 ⭐留言评论 📝私信必回哟😁

🍉博主将持续更新学习记录收获,友友们有任何问题可以在评论区留言

📍内容导读📍

C++ pointer-like classes

🍅namespace经验之谈

🍅转换函数

🍅智能指针

🍅迭代器

🍅仿函数

C++模板

🍅类模板

🍅函数模板

🍅成员模板

🍅模板特化

🍅模板模板参数


C++ 11

namespace经验之谈
  • 在编写库或框架时,可以使用命名空间来避免与用户代码的命名冲突。通过将库的所有内容放在一个命名空间中,可以确保库的名称不会与用户代码中的其他名称冲突。

  • 避免在头文件中使用using namespace语句。在头文件中使用using namespace语句会将整个命名空间的内容引入到全局作用域中,可能导致命名冲突和代码的不可预测性。最好的做法是在源文件中使用using语句,或者使用命名空间限定符来访问命名空间中的内容。

  • 命名空间可以嵌套使用,以创建更复杂的命名空间结构。嵌套的命名空间可以更好地组织代码,并提供更好的可读性。

  • 当使用第三方库时,可以将其放在一个自定义的命名空间中,以避免与自己的代码或其他第三方库的命名冲突。

转换函数

1. 转换函数没有返回类型,但在函数声明前加上了要转换的目标类型。

2.  转换函数必须是类的成员函数,不能是静态函数。

3.  转换函数没有参数。

4.  转换函数可以被隐式调用,也可以被显式调用

class Fraction
{
    public:
        Fraction(int num,int den=1)
        : m_numerator(num), m_denominator(den) {   }
-----------------------------------------转换函数
        operator double() const {   // const
            return (double) (m_numerator / m_denominator);
        }
-----------------------------------------
private:
    int m_numerator;   //分子
    int m_denominator; //分母
};   
智能指针

        Pointer-like classes是指那些行为类似指针的自定义类。这些类在使用上具有类似指针的语义,可以进行解引用、指针算术运算等操作。

  1. 解引用操作符(*):重载解引用操作符,使得对象可以被解引用。例如,T& operator*() const

  2. 成员访问操作符(->):重载成员访问操作符,使得对象可以像指针一样使用箭头操作符。例如,T* operator->() const。 

  3. 指针算术运算符:重载指针算术运算符,使得对象可以进行指针的加减操作。例如,T* operator+(int n) const

  4. 比较运算符:重载比较运算符,使得对象可以进行指针的比较操作。例如,bool operator==(const self& other) const

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;
....
};
迭代器

        迭代器是一种指向容器中元素的对象,类似于指针的行为。迭代器允许我们遍历容器中的元素,并对其进行访问和操作。因此,迭代器可以被认为是一种指向容器元素的指针。

template<class T, class Ref, class Ptr>
struct _list_iterator{
    typedef  _list_iterator<T, Ref, Ptr> self;typedef Ptr pointer; typedef Ref reference;
    ...
    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),date; }
    pointer operator->() const { return &(operator*()); }
    self& operator++() { ...}
    self& operator++(int) {...}
  1. 解引用操作符(*):重载解引用操作符,使得迭代器可以被解引用,获取当前指向的元素。例如,T& operator*() const

  2. 成员访问操作符(->):重载成员访问操作符,使得迭代器可以像指针一样使用箭头操作符,访问当前指向的元素的成员。例如,T* operator->() const

  3. 前置递增运算符(++):重载前置递增运算符,使得迭代器可以向前移动到下一个元素。例如,MyIterator& operator++()

  4. 后置递增运算符(++):重载后置递增运算符,使得迭代器可以向前移动到下一个元素,并返回移动前的迭代器。例如,MyIterator operator++(int)

  5. 比较运算符:重载比较运算符,使得迭代器可以进行指针的比较操作。例如,bool operator==(const MyIterator& other) const

 仿函数

        仿函数(Function-like classes)是一种重载了函数调用运算符(operator())的类,使其可以像函数一样被调用。这种类的对象可以像函数一样接受参数并返回结果。

#include <iostream>

class Adder {
public:
    int operator()(int a, int b) {
        return a + b;
    }
};

int main() {
    Adder add;
    int result = add(3, 4);
    std::cout << "Result: " << result << std::endl;
    
    return 0;
}

       有一些奇特的基类(base classes)用于定义和实现仿函数(Function Object)如下所例:std::unary_function和std::binary_function: 这两个基类是过时的,自C++11起已被弃用。它们用于定义一元和二元仿函数的参数类型和返回类型。在C++11之后,可以直接使用函数调用运算符(operator())的参数和返回类型来定义仿函数。

auto

        auto是一个关键字,用于声明变量的类型自动推导。通过使用auto关键字,编译器可以根据变量的初始化值自动推断出变量的类型,从而简化代码并提高可读

list<string>c;
...
auto ite = find(c.begin(), c.end(), target);//赋值的时候,auto 会自动推出类型
											//声明变量的时候不能使用auto, 推不出来类型
 range-base for loop

        range-based for循环是一种用于遍历容器或其他可迭代对象的循环结构。它提供了一种更简洁和直观的方式来遍历元素,而不需要使用索引或迭代器。

for (element : container) {     // 循环体      }

for (int i : {2, 3, 5, 7, 9, 13, 17, 19}){
	cout<< i << endl;
}

vector<double> vec;
...
for ( auto elem : vec ) {//pass by value
	cout <<elem <<endl;
}
for ( auto& elem : vec ) {//pass by reference
	elem *= 3;
}
lambda 

C++模板

类模板

        类模板是一种用于定义通用类的机制。通过类模板,可以定义一个通用的类模板,并使用类型参数来表示类中的数据类型或成员函数的参数类型。

template<typename T>
class complex
{
public:
	complex( T r = 0,T i = 0): re (r), im (i){ }
	comp1ex& operator+=(const complex&);
	T real () const { return re; }
	T imag () const { return im; }

private:
	T re,im ;
	friend complex& _doapl (complex* , const complex&);
};

 {
    complex<double>c1(2.5,1.5);//使用方法
    complex<int> c2(2,6);
    ...
}

函数模板

        函数模板是一种用于定义通用函数的机制。通过函数模板,可以定义一个通用的函数模板,并使用类型参数来表示函数中的参数类型。这样,可以在不同的上下文中使用相同的函数模板,只需提供不同的类型参数。

class stone
{
public:
	stone(int w,int h, int we): _w ( w ),_h (h), _weight (we){}
	bool operator<(const stone& rhs) const { return _weight < rhs._weight; }
	
private:
	int _w, _h,_weight;
};

template <class T>
inline
const T& min (const T&a, const r& b)
{
	return b < a ? b : a ;
}
成员模板

        成员模板是一种在类中定义的模板函数或模板成员函数。它允许在类中使用通用的函数模板,并将其作为类的成员函数。

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) { }     //把初值的头尾放进来,放入本身的头尾
													//把子类赋给对应的父类,up-cast
};
  1. 成员模板可以在类的内部定义,也可以在类的外部定义。如果成员模板在类的内部定义,它将自动成为类的成员函数。如果成员模板在类的外部定义,需要使用类的作用域解析运算符(::)来指定它是类的成员函数。

  2. 成员模板可以使用类的类型参数,也可以使用成员函数的类型参数。

  3. 成员模板可以访问类的私有成员。

  4. 成员模板可以被特化。

  5. 成员模板可以被重载。

模板特化

        模板特化是指为特定的类型提供特定的模板实现。当需要针对某些特定类型的参数,提供不同的实现时,可以使用模板特化。

        完全特化:完全特化是指为特定的类型参数提供完全不同的实现。完全特化的语法是在模板名称后面加上尖括号,并在尖括号内指定特定的类型参数。

template <class Key>//泛化
struct hash { };

//特化
template<>
struct hash<char> {
	size_t operator () (char x) const { return x; }
};

template<>
struct hash<char> {
	size_t operator () (int x) const { return x; }
};

template<>
struct hash<char> {
	size_t operator () (long x) const { return x; }
};

        偏特化:是模板特化的一种特殊形式,一般来说,所谓的特化是指将所有的模板参数进行替换成具体类型,而偏特化是指将部分参数类型指定为具体类型。

范围的偏个数的偏
模板模板参数

        模板模板参数(Template Template Parameter)是C++中的一种模板技术,允许在定义模板时使用另一个模板作为其参数。

        例如,假设有一个模板类template<typename T, template<typename> class Container>,其中Container是一个模板模板参数,它接受一个类型参数,并用于定义一个容器类。这样,我们可以在使用这个模板类时,通过不同的容器模板作为Container来实例化不同类型的容器。

variadic templates(数量不定的模板参数)

        C++中的一种特性,允许函数和类接受任意数量的不同类型的参数。...就是一个所谓的pack(包)属于语法

C++对象模型

复合&继承关系下的构造和析构

C++面向对象高级编程(上)_LS_o.0的博客-CSDN博客

vptr 虚指针和 vtbl 虚表再现

        

动态绑定必须满足三个条件,(而静态绑定一定调用到某个地址。),实现 多态

  1. 必须是用指针来调用,如上面的 p 指针;
  2. 这个指针是向上转型的(祖先);
  3. 调用的是虚函数

this指针,动态绑定

关于Dynamic Binding

现在考虑一下,为什么动态绑定解析成C语言形式会是:

(*(p->vptr)[n])(p)  //第二个p其实就是this指针(因为p是调用者)
//或
(* p->vptr[n])(p)

下图中 a是一个对象,它调用函数是一个静态绑定,可以看到汇编呈现的就是:Call xxx一个地址 

下图中pa满足动态绑定的三个条件,所以它是一个动态绑定,而在汇编语言中,汇编所呈现出来的那部分命令就等价于C语言中的(*(p->vptr)[n])(p)

重载new([ ])/delete([ ])

        重载newdelete运算符来自定义内存的分配和释放行为。通过重载这两个运算符,我们可以使用自定义的内存管理策略,例如从特定的内存池中分配内存,或者进行内存跟踪和调试。

重载new运算符的一般语法形式如下: size参数表示要分配的内存大小,函数的返回类型是void*,表示分配的内存地址。

void* operator new(size_t size) {
    // 自定义的内存分配逻辑
    // 返回分配的内存地址
}
void* operator new[](size_t size) {
    // 自定义的内存分配逻辑
    // 返回分配的内存地址
}

重载delete运算符的一般语法形式如下: ptr参数表示要释放的内存地址。

void operator delete(void* ptr) {
    // 自定义的内存释放逻辑
}
void operator delete[](void* ptr) {
    // 自定义的内存释放逻辑
}
重载 member operator new([ ])/delete([ ])

重载类的成员new运算符的一般语法形式如下:

void* ClassName::operator new(size_t size) {
 // 自定义的内存分配逻辑
 // 返回分配的内存地址
 }

重载类的成员delete运算符的一般语法形式如下:

void ClassName::operator delete(void* ptr) {
    // 自定义的内存释放逻辑
}
 placement new / delete

        参数列表的第一个参数必须为size_t,其余参数以new所指定的placement arguments为初值。出现在new(......)小括号内的便是所谓的placement arguments。

        只有当new所调用的构造函数(new被分解的第一步)抛出异常,才会调用与new对应的那个重载operator delete(),主要用来归还未能完全创建成功的对象所占用的内存。

placement new的应用:Basic_String使用new(extra)扩充申请量

        std::basic_string是C++标准库中的字符串类模板,它在内部使用动态分配的内存来存储字符串数据。当字符串的长度超过当前内存容量时,std::basic_string会重新分配更大的内存,并将原有的数据复制到新的内存位置。为了避免频繁的内存分配和复制操作,std::basic_string使用了placement new来在预先分配的内存块中构造新的字符串对象。这个预先分配的内存块通常称为"extra"空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值