网教网

搜索
查看: 91|回复: 0

候捷c+ +面向对象高级编程笔记(下)

[复制链接]

1

主题

2

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2023-4-19 18:50:07 | 显示全部楼层 |阅读模式
面向对象高级编程(下)

转换函数

将其他类型转换为类:



对于下面的4,我们可以调用绿色部分代码将其转换为Fraction类
将类转换为其他类型:



转换函数书写的方式类似操作符重载,只不过这里重载的是类型。
调用这个函数的时候我们可以隐式的调用这个函数,而也正因为如此可能会发生如下的错误:


在这种情况下,如下两种操作都是合理的:

  • 将f转为double类型(黄色部分)
  • 将4转换为Fraction类型(绿色部分)
    而如此将会产生歧义从而报错,而为了避免这种情况引申出了关键字:explicit
    explicit关键字:



在c++中,explicit只能用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换。
通过explicit对构造函数的声明,使4不会隐式的调用构造函数返回成Fraction类型
智能指针




一种像指针的类,在内部实现的对 * 和 -> 的重载

  • 对于 * 的重载,我们返回的是reference
  • 对于 -> 的重载,我们返回的是pointer
    但有一点需要注意,当调用 -> 时,这个操作符不会被消耗,而是继续作用下去。
    上图中先调用 sp-> 返回的应该是px。照理说接下来应该是px method(),而实际上 -> 并没有消失而是继续作用在px身上
    迭代器



由红色框和黄色部分可知,这也是个智能指针,但与其他不同的是他还需要重载++,--符号。
接下来分析一下黄色部分的重载:






对于 * 的重载,我们返回的是(*node).data,也就是说返回的是一个数据类型为T的对象
对于 -> 的重载,我们需要返回一个数据类型为T的对象的指针,所以需要在 (*node).data 的基础上&
仿函数




行为像函数的一个类,实现了对()的重载
使用示例:
class test {
public:
    test(int x, int y) : a(x), b(y) {}
    int operator ()(int x){return a+b;}
    int a;
    int b;
};
int main() {
    test x(1,4);
    cout<<x(0)<<endl;
    cout<<test(3,8)(7);
}
模板
类模板
类模板简单示例:
template<typename T>
class test{
public:
    test(T x,T y):a(x),b(y){}
    T sum(){return a+b;}
private:
    T a,b;
};

int main(){
    test<double> k(1.54,1.003);
    cout<<k.sum();
}
函数模板




与类模板相比,函数模板显然更为智能。在调用函数时我们不必指明type,编译器会为我们自行推到type
成员模板




在模板的内部又声明了一个模板
示例1




通过成员模板去描述继承的关系
示例2




智能指针相关的
模板特化
全特化



namespace case1 {
template <class T>
class test {};
template <>
class test<int> {
public:
    string operator()() { return "这是int\n"; }
    int a;
};
template <>
class test<double> {
public:
    string operator()() { return "这是double\n"; }
    int a;
};
}  // namespace case1
int main() {
    case1::test<int> k;
    cout << k();
    cout << case1::test<double>()();
}
偏特化
个数的偏特化



#include <iostream>
namespace case2 {

template <typename T1, typename T2>
class test {
public:
    string operator()(){return "这是没有特化的\n";}
};
template<typename T>
class test<int,T>{
public:
    string operator()(){return "这是偏特化---int\n";}
};

}  // namespace case2

int main() {
    cout<<case2::test<double,double>()();
    cout<<case2::test<int,double>()();
    cout<<case2::test<int,float>()();
}
/*结果:
这是没有特化的
这是偏特化---int
这是偏特化---int
*/
范围的偏



namespace case3 {
template <typename T>
class test {
public:
    string operator()() { return "这是没有特化的\n"; }
};
template <typename T>
class test<T*> {
public:
    string operator()() { return "这是范围偏特化的---指针\n"; }
};
}  // namespace case3
int main() {
    cout<<case3::test<string>()();
    cout<<case3::test<string*>()();
}
/*结果:
这是没有特化的
这是范围偏特化的---指针
*/
模板模板参数



template <typename T, template <typename U> class Container>
class XCls {
private:
    Container<T> c;
};

template <typename T>
class test {
private:
    T t;
};

//因为list容器实质上是有第二个参数的,所以我们用如下方法定义:
template <typename T>
using Lst = std::list<T, std::allocator<T>>;

int main(void) {
    XCls<std::string, test> mylst1;  //用定义的一个模板类传入
    XCls<std::string, Lst> mylst2;   //传入容器
return 0;
}
另一种情况(注意,这个不是模板模板参数)




数量不定的模板参数




书写不定模板参数时需要注意 ... 要一直都写!!!
void Print(){}
template <typename T, typename... Types>
void Print(const T& firstarg, const Types&... args) {
    cout<<firstarg<<endl;
//以下两种方式都可:
//Print(args...);//重载参数列表为空的时候
if(sizeof...(args))Print(args...);//对剩余args的个数进行判定
}
int main() {
    Print(8,21,2.423,"ASdfa",'D');
}
reference细析




引用与指针的不同:

  • 在大小方面,引用的大小是和对象保持一致的,这和指针只保存地址不同
  • 对于使用后更改方面:

    • 指针初始化时指向了一个对象,日后也可以指向其他对象
    • 当引用初始化时指向了一个对象,此时引用相当于该对象的别名,换句话说此时引用和对象形同一体,也就无法更改引用指向其他对象




因为上面已经提到引用绑定对象后相当于和对象形同一体,所以在调用方法的时候和对象的操作是相同,和指针的操作是不同的。而也正是因为行同一体这点,当我们想对函数进行重载时也应该注意 & 对象 和 对象 实际上是等价的,也就是上面图片说的签名是相同的。
虚指针和虚函数
内存状态
下图阐述了虚函数在内存中的存在状态




当类里面有虚指针时,在类的内存空间中就会多个指针指向虚函数表
动态绑定






再谈const




const也是函数签名的一部分,也就是说可以重载有const的和没有const的两个版本的函数。
当上述两个函数都同时存在时,const对象只会调用const版本的,反之亦然。
当const对象调用非const的函数时会出错。
再谈new,delete
作用域





  • 如果使用某个类的成员函数来重载这些运算符,则意味着这些运算符仅针对该特定类才被重载。
  • 如 果重载是在类外完成的(即它不是类的成员函数),则只要您使用这些运算符(在类内或类外),都将调用重载的“ new”和“ delete”。 这是全局超载。
    示例



new:




  • new不带虚函数的类时,其内存空间除了存有数据外还有一块记录大小的内存块。
  • new带虚函数的类时,其内存空间除了存有数据外还有一个指针。
    delete:



调用delete时发现都没有进入自己写的重载delete中去
重载的要求:




对于new而言:

  • 我们可以重载多个版本的new
  • new的参数第一个必须是size_t
  • 但是在调用时这个size_t不用指明
    对于delete而言L:
  • 我们同样可以重载多个版本的delete
  • 我们重载的delete不会被delete调用,只有当new抛出异常时才会调用我们所重载的delete
重载示例:




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表