0%

2020年3月2日 下午11:01

详解派生类构造函数与析构函数 - Acelit - 博客园

c++特殊的地方:

  1. 派生类的构造函数:
    • 派生类不能继承基类的构造函数,必须自己定义构造函数进行新增数据成员初始化工作,如果想同时初始化基类数据成员,必须调用基类构造函数。
  2. 派生类的析构函数:
    • 正如派生类不能继承基类的构造函数,派生类也不能继承基类的析构函数,派生类的清理工作由派生自身析构函数负责,基类的清理工作由基类析构函数负责。
    • 析构函数的调用顺序正好和构造函数调用顺序相反,即先调用派生类析构函数清理新增的成员,再调用子对象析构函数(基类析构函数)清理子对象,最后再调用基类析构函数清理基类成员。
    • 注意:有一种情况下不用再派生类中指定析构函数也可以正常的析构:
      • 实现的方式是通过虚函数表!
      • 基类析构函数声明为虚函数
        • 该基类所有派生类也将自动成为虚函数,所有虚析构函数的入口地址都会存放在一个虚函数表(指针数组)中,查找方便,这样就避免了无法调用派生类析构函数所造成的内存泄漏问题了
      • 这里面的具体逻辑我不知道,过段时间我肯定忘了,这个知识点目前就死记忆吧!

2020年3月2日 下午10:30
C++ 多态 | 菜鸟教程

C++多态定义:

  1. C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数

C++实现多态的两个条件:

  1. 继承
  2. 基类使用虚函数声明

总结:

  1. 如果在基类中使用vitual虚函数,::那么编译器看的是指针的内容,而不是它的类型::。
    1. 编译器看的是指针的内容 == > 动态绑定
    2. 编译器看的是类型 == > 静态绑定
    3. 代码指的就是下面的核心句1-2。

基类的定义-1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream> 
using namespace std;

class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
int area()
{
cout << “Parent class area :” <<endl;
return 0;
}
};

基类的定义-2:【使用virtual】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream> 
using namespace std;

class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << “Parent class area :” <<endl;
return 0;
}
};

继承类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Rectangle: public Shape{
public:
Rectangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << “Rectangle class area :” <<endl;
return (width * height);
}
};
class Triangle: public Shape{
public:
Triangle( int a=0, int b=0):Shape(a, b) { }
int area ()
{
cout << "Triangle class area :" <<endl;
return (width * height / 2);
}
};

主函数定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 程序的主函数
int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);

// 核心句1:存储矩形的地址
shape = &rec;
// 核心句2:调用矩形的求面积函数 area
shape->area();

return 0;
}

2020年3月2日 下午10:19
C++很“虚” - Acelit - 博客园
注:结合代码看,容易懂很多!

使用c++四“虚“的前提

作用:

  1. 虚基类:间接派生类只保存共同基类的一份成员(数据成员/函数成员),优化存储空间
  2. 虚函数:虚函数允许在派生类中重新定义与基类同名的函数,并且允许通过基类指针或引用来访问基类和派生类的同名函数
  3. 虚析构函数:防止内存泄漏
    • 派生类析构函数无法从基类继承,在没有声明基类析构函数为虚函数时,基类指针释放时无法找到派生类析构函数地址,也就不能释放派生类对象所在内存空间。而将基类析构函数也声明为虚函数时,该基类所有派生类也将自动成为虚函数,所有虚析构函数的入口地址都会存放在一个虚函数表(指针数组)中,查找方便,这样就避免了无法调用派生类析构函数所造成的内存泄漏问题了

总结:

  1. 何时我们需要考虑给基类声明虚析构函数
    1. 虚析构函数是建立在虚函数的基础之上的,即在想使用基类指针访问派生类对象时必须要声明基类虚析构函数,不管基类是否需要析构函数;
  2. 因为虚函数表会占据一定的空间开销,在不存在上述1中情况时没有必要使用虚函数;
  3. 多态性:因为编译器只做静态的语法检查,无法确定调用对象,运行时才确定关联关系,所以多态性又分为静态多态性和动态多态性。
    • 静态多态性(编译时的多态性,静态关联)是指在程序编译时就能够确定调用的是哪个函数,函数重载_运算符重载_通过对象名调用的虚函数都属于静态关联
    • 动态多态性(运行时多态性,动态关联,滞后关联)是指只有在程序运行时才能够确定操作的对象,通过虚函数实现。

补充:

  1. 虚函数是动态多态的基础!(不适用虚函数时,其实就是静态多态)

2020年3月2日 下午9:35

C++面试题之浅拷贝和深拷贝的区别
浅拷贝和深拷贝的区别? - Acelit - 博客园
注:结合代码看,容易懂很多!

总结:

  1. 浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,
  2. 深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

再说几句:

当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,还应该考虑以下两种情形:

  1. 当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;
  2. 当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。

最后:这里就引出了智能指针!!!

浅拷贝带来问题的本质在于析构函数释放多次堆内存,使用std::shared_ptr,可以完美解决这个问题

2020年2月28日 下午12:20

契约式设计

  • 期望所有调用它的客户模块都保证一定的进入条件:这就是函数的 先验条件 —客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况。
  • 保证退出时给出特定的属性:这就是函数的 后验条件 —供应商的义务,显然也是客户的权利。
  • 在进入时假定,并在退出时保持一些特定的属性: 不变条件

白板编程浅谈——Why, What, How | lucida

认为程序员可以被分为两种:

  • 先确认前条件_不变式_终止条件/边界条件,然后写出正确的代码
  • 先编写代码,然后通过各种用例_测试_调试对程序进行调整,最后得到似乎正确的代码

白板编程的目标并不是要求面试者一下子写出完美无缺的代码,而是:

  • 让面试者在解题的过程中将他/他的思维过程和编码习惯展现在面试官面前,以便面试官判定面试者是否具备清晰的逻辑思维和良好的编程素养
  • 如果面试者陷入困境或是陷阱,面试官也可以为其提供适当的辅助,以免面试陷入无人发言的尴尬境地

1.确定需求

  1. 面试者在白板编程时最重要的任务是理解题目,确认需求——确定输入_输出,确定数据范围,确定时间_空间要求,确定其它限制。以最常见的排序为例:
    • 输入:来自数组?链表?或是不同的机器?
    • 输出:是否有重复?是否要求稳定?
    • 数据范围:排序多少个元素?100 个? 100 万个? 1 亿个?这些元素是否在某个范围内?
    • 时间要求:1 分钟?1 刻钟?一小时?
    • 空间要求:是否常量空间?是否可以分配新的空间?如果可以,能分配多少空间?是否在内存中排序?
    • 其它限制:是否需要尽可能少的赋值?是否需要尽可能少的比较?
  2. 有时面试官不会把题目说的特别清楚,这时就需要面试者自己去确认这些需求,不要认为这是在浪费时间,不同的需求会导致截然不同的解法,此外确认需求会留给面试官良好的印象。

2.白板编程

理解题目确认需求之后,面试者就可以开始在白板上编写代码,下面是一些我自己的白板编程经验:

  1. ::先写出轮廓(大纲)::
    • 白板编程没法复制粘贴,所以后期调整代码结构非常困难。因此我们最好在开头写出程序的大致结构,从而保证之后不会有大改;
  2. ::确定前条件_不变式_后条件::
    • 我们可以通过注释的形式给出代码的前条件_不变式_后条件
  3. 使用实例数据验证自己的程序
    • 尽管不变式足以验证程序的正确性,但适当的使用实例数据会大大增强代码的可信性
  4. 使用缩写
    • 白板编程并不需要面试者在白板上写出能够一次通过编译的代码。为了节省时间,面试者可以在和面试官沟通的基础上使用缩写。例如使用 Iter 替代 Iterable,使用 BQ 替代 BlockingQueue。(此法尤其适合于 Java -_-#)
  5. 至少留一行半行宽
    • 出于紧张或疏忽,一般面试者在白板编程时会犯下各种小错误,例如忘了某个判断条件或是漏了某条语句,空余的行宽可以帮助面试者快速修改代码,使得白板上的代码不至于一团糟。

3.不会做怎么办

相信大多数面试者都碰到过面试题不会做的情况,这里说说我自己的对策:

  1. 至少先给出一个暴力(Brute force)解法
  2. 寻找合适的数据结构(例如栈_队列堆_图)和算法(例如分治_回溯_动态规划/贪婪)
  3. 从小数据集开始尝试
  4. 如果还是没有头绪,重新考虑题目的前条件,思考是否漏掉了条件(或是隐含的条件)
  5. 如果 3 分钟过后还是没有任何思路,请求面试官提示,不要觉得不好意思——经过提示给出答案远强于没有答案

如何阅读书籍 | lucida

2020年2月28日 下午1:06
如何阅读书籍 | lucida

  1. 我是一个功利主义者( Utilitarianism ),因此我认为阅读的目标在于为自己创造实际价值,所以:
    • 我不会因为某本书看起来很有趣就去阅读(机会成本)。
    • 也不会因为很多人推荐某本书就去阅读(从众)。
    • 更不会因为某本书难就去阅读(追求智商优越感)
  2. 一本书值得阅读,当且仅当:
    • 它可以直接为我创造价值。
    • 它可以间接为我创造价值。
  3. 我的阅读目标:
    • 形成T型知识结构:专业知识尽可能深入,专业周边知识尽可能精炼。

2020年2月24日 下午4:22

OLVPS-外网访问
Cloudflare - The Web Performance & Security Company | Cloudflare

简要步骤:

  1. 在OLVPS中搭建shadowsocks
    1. OLVPS所有的ip都是被ban的,不论怎么更换。更换一定次数之后就不能更换了。
    2. 在被ban的情况下ssh连接,可以先在vutlr中申请一个服务器,通过vultr去连接。
    3. 我尝试了一下:直接从vultr去pingOLVPS是非常快的,在100ms以下。
  2. 在CloudFlare中
    1. 提交一个域名
    2. 在域名注册商中更换默认的域名服务器
    3. 在DNS选项中,配置域名->OLVPS_ip的解析,并选择proxy
      1. 注:这里可以尝试间断性的开关一下proxy选项,有时候一次不成功。
    4. SSL中选择flexible
  3. ping 自己的域名,看是否可以解析到OLVPS_IP
    1. 有时候会解析到CloudFlare的服务器ip,这时候要在CloudFlare中更新检查
      1. OverView是否显示:Great news! Cloudflare is now protecting your site,否则recheck一下
      2. DNS:proxy重新开关一下
      3. SSL:是否是flexible
    2. 这个ping是300多,可见这个CDN速度并不是很好,看视频上午都没戏!