C++继承和多态

2023-11-04

C++继承和多态

继承

继承的本质:

  1. 代码的复用。
  2. 在基类中给所有派生类提供统一的虚函数接口,让派生类进行重写,然后就可以使用多态了。

类和类的关系:a part of … …一部分关系

继承:a kind of … …一种的关系

继承引入了一些概念:基类/父类 派生类/子类

类的私有属性只有当前类的方法或者友元才能访问,继承后的子类不管哪种方式都无法访问

基类成员的访问限定,在派生类里面是不可能超过继承方式的

总结:

  1. 外部只能访问对象的public的成员,protected和private的成员无法直接访问。
  2. 在继承结构中,派生类从基类可以继承过来private的成员,但是派成类无法直接访问(私有成员在派生类中不可见)。
  3. protected和private的区别?在基类中定义的成员,想在派生类访问,但是不想被外部访问,那么在基类中,把相关成员定义成protected保护的;如果派生类和外部都不打算访问,那么在基类中,就把相关成员定义成private私有的。

**默认的继承方式是什么?**要看派生类是用class定义的,还是struct定义的?class定义派成类,默认继承方式就是private私有的。struct定义派生类,默认继承方式就是public公有的

派生类的构造过程

派生类怎么初始化从基类继承来的成员变量呢?

  1. 派生类从基类可以继承来所有的成员(变量和方法),除过构造函数和析构函数(因为构造函数和析构函数只有在当前类中才有作用)。通过调用基类相应的构造函数来初始化,派生类的构造函数和析构函数,负责初始化和清除派生类部分。派生类从基类继承来的成员的初始化和清理由基类的构造函数和析构函数来负责。
  2. 派生类和基类的构造和析构顺序?一般情况下基类的初始化在派生类构造函数的初始化列表中初始化,所以先调用基类的构造函数,在调用派生类的构造函数,来初始化自己的成员。出作用域时,先调用派生类的析构函数,在调用基类的析构函数。
#include<iostream>
using namespace std;

class Base 
{
public:
	Base(int data) :ma(data) { cout << "Base(int)" << endl; }
	~Base() { cout << "~Base()" << endl; }
protected:
	int ma;
private:
};
class Derive:public Base
{
public:
	/*
	“Base”: 没有合适的默认构造函数可用
	Derive(int data) :ma(data), mb(data) { cout << "Derive()" << endl; }
	*/
	Derive(int data) :Base(data), mb(data) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
protected:
private:
	int mb;
};
int main()
{
	/*//先调用基类的构造,在调用子类的构造,出作用时先调用子类的析构,在调用父类的析构
	Base(int)
	Derive()
	~Derive()
	~Base()
	*/
	Derive d(20);

	return 0;
}

重载、隐藏和覆盖

重载关系:一组函数要重载,必须处于同一作用域中;而且函数名字相同,参数列表不同。Base类中的两个show函数就是重载的关系。

隐藏(作用域的隐藏)关系:在继承结构当中,派生类的同名成员,把基类的同名函数给隐藏调用了。Derive类中的show方法隐藏了Base类中的show方法和show(int)方法。

覆盖关系:基类和派生类的方法,返回值、函数名以及参数列表都相同,而且基类的方法都是虚函数,那么派生类的方法就会自动处理成虚函数,它们直接称为覆盖关系。具体查看虚函数章节内容的动态绑定。

把继承结构,也就说成从上(基类)到下(派生类)结构。

基类对象 -> 派生类对象 N

派生类对象 -> 基类对象 Y

基类指针(引用) -> 派生类对象 N

派生类指针(引用) -> 基类对象 Y

在继承结构中,进行上下的类型转换,默认只支持从下到上类型的转化,也就是派生类转化成基类

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int data=10) :ma(data) { cout << "Base(int)" << endl; }
	~Base() { cout << "~Base()" << endl; }
	void show() { cout << "Base::show()" << endl; }
	void show(int) { cout << "Base::show(int)" << endl; }
protected:
	int ma;
private:
};
class Derive :public Base
{
public:
	Derive(int data=10) :Base(data), mb(data) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	void show() { cout << "Derive::show()" << endl; }
	//void show(int) { cout << "Derive::show(int)" << endl; }
protected:
private:
	int mb;
};
int main()
{
	Derive d;
	d.show();
	//“Derive::show”: 函数不接受 1 个参数	
	//d.show(10);//如果Derive中没有重写Base中的void show(int)函数,则会报错
	d.Base::show(10);//显示调用父类的带参数的成员函数

	Base b(10);
	Derive de(20);
	// 基类对象b  <-  派生类对象de  类型从下往上的转化   Y
	b = de;
	// 派生类对象de  <-  基类对象b  类型从上往下的转化   N
	//de = b;
	// 基类指针(引用)  <-  派生类对象  类型从下往上的转化   Y
	Base* p = &de;
	p->show();
	((Derive*)p)->show();
	
	// 派生类指针(引用)  <-  基类对象  类型从上往下的转化   N
	//Derive* pd = &b;//存在内存的非法访问

	return 0;
}

静态绑定/动态绑定/虚函数

静态绑定

静态编译时期的绑定,也称为静态绑定,编译期间已经确定函数的调用情况。

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int data = 10) :ma(data) { cout << "Base(int)" << endl; }
	~Base() { cout << "~Base()" << endl; }
	void show() { cout << "Base::show()" << endl; }
	void show(int) { cout << "Base::show(int)" << endl; }
protected:
	int ma;
private:
};
class Derive :public Base
{
public:
	Derive(int data = 10) :Base(data), mb(data) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	void show() { cout << "Derive::show()" << endl; }
protected:
private:
	int mb;
};
int main()
{
	Derive d(10);
	Base* p = &d;

	//静态编译时期的绑定,也称为静态绑定,编译期间已经确定函数的调用情况
	p->show();//call        Base::show (09F1041h)
	p->show(10);//call        Base::show (09F1433h)

	cout << sizeof(Base) << endl;
	cout << sizeof(Derive) << endl;

	cout << typeid(p).name() << endl;
	cout << typeid(*p).name() << endl;

	return 0;
}

虚函数 && 动态绑定

一个类里面定义了虚函数,那么编译阶段编译器给这个类类型产生一个唯一的vftable(虚函数表),虚函数表中主要存储的内容就是RTTI指针和虚函数的地址。RTTI(run-time type information运行时的类型信息,类型字符串(class Base等))当程序运行时,每一张虚函数表都会加载到程序的.rodata(只读数据)区。

一个类里面定义虚函数,那么这个类定义的对象,其运行时,内存中开始部分,多存储一个vfptr(虚函数指针),指向相应类型的虚函数表vftable。一个类型定义的n个对象,他们的vfptr指向的都是同一张虚函数表

一个类里面虚函数的个数,不影响对象的内存大小(vfptr指针大小还是指针类型大小),影响的是虚函数表的大小

如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,而且基类的方法是virtual虚函数,那么派生类的这个方法将会自动处理成虚函数。这个派生类这个方法就重写/覆盖了基类的方法,这个派生类的vftable表中的对应虚函数的地址替换基类的虚函数地址

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int data = 10) :ma(data) { cout << "Base(int)" << endl; }
	~Base() { cout << "~Base()" << endl; }
	virtual  void show() { cout << "Base::show()" << endl; }
	virtual void show(int) { cout << "Base::show(int)" << endl; }
protected:
	int ma;
private:
};
class Derive :public Base
{
public:
	Derive(int data = 10) :Base(data), mb(data) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	void show() { cout << "Derive::show()" << endl; }
protected:
private:
	int mb;
};
int main()
{
	Derive d(10);
	Base* p = &d;

	/*
	* Base::show();此时是一个虚函数,所以就需要动态绑定了,通过查找对象的vfptr进行动态绑定
	mov         ecx,dword ptr [p]  
	mov         eax,dword ptr [edx+4]  
	call        eax(虚函数地址),运行时期的动态绑定
	*/
	p->show();
	p->show(10);

	//因为有了虚函数,所以添加了虚函数指针,导致多了4个字节
	cout << sizeof(Base) << endl;
	cout << sizeof(Derive) << endl;

	cout << typeid(p).name() << endl;
	/*
	pb的类型:Base  ->  有没有虚函数
	如果Base没有虚函数,*pb识别的就是编译时期的类型,  *pb  等价于  Base类型
	如果Base有虚函数,*pb识别的就是运行时期的类型 RTTI 类型
	pb -> d(vfptr) -> Derive vftable -> class Derive
	*/
	cout << typeid(*p).name() << endl;

	return 0;
}


cl virtual.cpp /d1reportSingleClassLayoutBase查看当前virtual.cpp文件中Base类的情况。


动态绑定

是不是虚函数的调用一定就是动态绑定? 肯定不是。

在类的构造函数中,调用虚函数,也是静态绑定(构造函数调用其他函数(虚函数),不会发生动态绑定),动态绑定依赖于对象(vfptr -> vftable)。如果不是通过指针或者引用变量来调用虚函数,那就是静态绑定。

对于存在虚函数的基类和派生类关系中:用对象本身去调用虚函数,发生的是静态绑定。不管是基类指针指向基类对象还是派生类对象,都发生动态绑定(必须由指针调用虚函数)。

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int data) :ma(data) { cout << "Base(int)" << endl; };
	virtual ~Base() { cout << "~Base()" << endl; }
	virtual void show() { cout << "Base::show()" << endl; }
protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data) :Base(data), ptr(new int(10)) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	virtual void show() { cout << "Derive::show()" << endl; }
private:
	int* ptr;
};
int main()
{
	Base b(10);
	Derive d(20);

	//这里的汇编是call Base::show/Derive::show 是静态绑定,用对象本身调用虚函数
	b.show();
	d.show();
	
	//不管是基类指针指向基类对象还是派生类对象,都发生动态绑定
	Base* pb1 = &b;
	pb1->show();
	Base* pb2 = &d;
	pb2->show();

	//不管基类引用的是基类的对象还是派生类的对象,都发生动态绑定
	Base& rb1 = b;
	rb1.show();
	Base& rb2 = d;
	rb2.show();

	//动态绑定
	Derive* pd1 = &d;
	pd1->show();
	Derive& rd1 = d;
	rd1.show();

	return 0;
}

虚析构函数

虚函数依赖

  1. 虚函数能产生地址,并存储在vftable中,其实也就是拥有虚函数的类产生的vftable。
  2. 对象必须存在(vfptr -> vftable -> 虚函数地址)

那些函数不能实现为虚函数?

  1. 构造函数。构造函数中(调用任何的函数,都是静态绑定的)调用虚函数,也不会发生静态绑定。
  2. 静态成员方法。静态方法不依赖于对象。

虚析构函数,析构函数调用的时候,对象是存在的!所以可以实现为虚函数。

基类的析构函数是虚函数,那么派生类的析构函数一定是虚函数

什么时候把基类的析构函数必须实现为虚函数?

基类的指针(引用)指向堆上new出来的派生类对象的时候,delete pb(基类指针),它调用析构函数的时候,必须发生动态绑定,否则会导致派生类的析构函数无法调用,导致内存泄漏

#include<iostream>
using namespace std;

class Base
{
public:
	Base(int data) :ma(data) { cout << "Base(int)" << endl; };
	virtual ~Base() { cout << "~Base()" << endl; }
	virtual void show() { cout << "Base::show()" << endl; }
protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data) :Base(data), ptr(new int(10)) { cout << "Derive()" << endl; }
	~Derive() { cout << "~Derive()" << endl; }
	virtual void show() { cout << "Derive::show()" << endl; }
private:
	int* ptr;
};
int main()
{
	Base* b = new Derive(10);
	//如果Base中的show不是虚函数,那么就是动态绑定,否则就会发生动态绑定
	b->show();
	//如果Base中的析构函数不是虚函数,那么就不会调用Derive中的析构函数,造成内存泄漏
	delete b;
	return 0;
}

抽象类

拥有纯虚函数的类叫做抽象类,抽象类不可以在进行实例化对象了,但是可以定义指针和引用变量。定义纯虚函数的方法virtual void function() = 0;,如果能进行抽象,那么尽量将函基类设计成抽象类。

基类的作用:

  1. 让派生类通过继承的方式复用基类的属性和方法。代码复用
  2. 给所有的派生类保留统一的覆盖/重写接口。虚函数/动态绑定
#include<iostream>
#include<string>
using namespace std;

class Animal
{
public:
	Animal(string name):_name(name){}
	virtual void bark() = 0;//纯虚函数,动态绑定和静态绑定问题
protected:
	string _name;
private:
};
class Dog:public Animal
{
public:
	Dog(string name):Animal(name){}
	void bark() { cout << _name << " bark: wang wang!" << endl; }
protected:
private:
};
class Cat :public Animal
{
public:
	Cat(string name) :Animal(name) {}
	void bark() { cout << _name << " bark: miao miao!" << endl; }
protected:
private:
};
void bark(Animal* p)
{
	p->bark();
}

int main()
{
	Cat c("小猫");
	Dog d("小狗");
	bark(&c);
	bark(&d);
	return 0;
}

多态

静态多态(编译时期的多态)、动态多态(运行时期的多态)

静态多态:函数重载、模板(函数模板和类模板)

bool compare(int,int){}
bool compare(double,double){}

//调用点
compare(10,20);call compare_int_int 在编译阶段就确定好调用的函数版本
compare(10,20);call compare_double_double

template<typename T>
bool compare(T a,T b){}

//调用点
compare<int>(10,20); -> 实例化一个compare<int>
compare<double>(10.0,20.0); -> 实例化一个compare<double>

动态多态:继承(虚函数)
在继承结构中,基类指针(引用)指向派生类对象,通过该指针(引用)调用同名覆盖方法(虚函数),基类指针指向哪个派生类对象,就会调用派生类对象的覆盖方法,称为多态。

多态底层是通过动态绑定来实现的,通过对象的vfptr去寻找vftable,再去调用方法。

继承和多态实际问题

对象的vfptr虚函数指针交换

#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
	Animal(string name) :_name(name) {}
	virtual void bark() = 0;//纯虚函数,动态绑定和静态绑定问题
protected:
	string _name;
private:
};
class Dog :public Animal
{
public:
	Dog(string name) :Animal(name) {}
	void bark() { cout << _name << " bark: wang wang!" << endl; }
protected:
private:
};
class Cat :public Animal
{
public:
	Cat(string name) :Animal(name) {}
	void bark() { cout << _name << " bark: miao miao!" << endl; }
protected:
private:
};

int main()
{
	Cat* c = new Cat("加菲猫");
	Dog* d = new Dog("二哈");

	//进行对象指针的强转,其实也就是交换对象第一个元素的值,
	//对象元素的分布是(vfptr和对应的属性),所以第一个元素是vfptr
	int* pp1 = (int*)c;
	int* pp2 = (int*)d;
	int temp = pp1[0];
	pp1[0] = pp2[0];
	pp2[0] = temp;
	
	//-----------------------------------------------------------------
	//对象的vfptr在上面操作被交换了
	c->bark();
	d->bark();
	//-----------------------------------------------------------------
	return 0;
}

虚函数默认参数不一致的情况

#include<iostream>
#include<string>
using namespace std;

class Base 
{
public:
	virtual void show(int i = 10) { cout << "Base::show(),i=" << i << endl; }
};
class Derive:public Base
{
public:
	virtual void show(int i = 20) { cout << "Derive::show(),i=" << i << endl; }
};
int main()
{
	Base* b = new Derive();
	/*
	动态绑定只是调用函数,其默认参数其实在编译过程中就已经压栈了,
	而压栈的默认参数就是对应类型的参数,Base::show(int i=10)
	*/
	b->show();//需要动态绑定,运行时根据vftable进行函数调用
	delete b;

	return 0;
}


函数权限更改后,虚函数调用情况

#include<iostream>
#include<string>
using namespace std;

class Base 
{
public:
	virtual void show() { cout << "Base::show()" << endl; }
};
class Derive:public Base
{
private:
	virtual void show() { cout << "Derive::show()" << endl; }
};
int main()
{
	Base* b = new Derive();//Derive中的show方法变为私有的,是否可以访问
	/*
	成员方法能不能调用,就是说方法的访问权限是不是public的,是在编译阶段就需要确定好的。
	最终能调用Derive::show()是在运行时期才确定的
	*/
	b->show();//需要动态绑定,编译阶段只能看见Base里面的show
	delete b;

	return 0;
}

虚继承

抽象类:有纯虚函数的类。vfptr/vftable

虚基类:继承过程中使用virtual修饰。vbptr/vbtable

virtual:修饰成员方法是虚函数。可以修饰继承方式,是虚继承。被虚继承的类,称作虚基类。

正常情况下的继承:

class A
{
public:
private:
	int ma;
};
class B:public A
{
public:
private:
	int mb;
};



class B:public A改为class B: virtual public A后,在派生类B中将会出现虚基类指针,并且会将基类的往后移(这里是特殊情况,一般继承的基类内容都在派生类前面)。并且在B的虚函数表中将会出现基类内容偏移的记录情况。

指针指向派生类对象,永远指向的是派生类基类部分数据的起始地址。所以这下面就会出现一个很严重的问题,内存释放起始的位置不对:

#include<iostream>
using namespace std;

class A
{
public:
	virtual void func() { cout << "A:func()" << endl; }
private:
	int ma;
};
class B:virtual public A
{
public:
	virtual void func() { cout << "A:func()" << endl; }
private:
	int mb;
};
int main()
{	
	A* p = new B();
	p->func();
	delete p;
	return 0;
}



这里执行了A* p = new B();,将p指向B的地址,所以会从B中虚基类的部分给A的指针p,所以在delete p;的时候,就会出现从B对象的中间位置开始释放内存,导致程序崩溃(只有设计new和delete出现才会出现问题,主要是windows的vs studio,linux的g++不会出现问题)。

多继承/菱形继承

多重继承中存在巨大的缺点,就是继承的多个基类中可能存在多个相同的成员属性和成员方法。派生类可能会存在多个重复的基类,这就可以采用虚继承来实现。

多重继承的问题,可以有更多的代码复用。

下方代码展示了一个菱形继承问题,会导致在初始化D对象的时候重复调用相同的A的构造函数,导致内存开辟的资源的消耗。

#include<iostream>
using namespace std;

class A
{
public:
	A(int data) :ma(data) { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
private:
	int ma;
};
class B:public A
{
public:
	B(int data) :A(data),mb(data) { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
private:
	int mb;
};
class C :public A
{
public:
	C(int data) :A(data), mc(data) { cout << "C()" << endl; }
	~C() { cout << "~C()" << endl; }
private:
	int mc;
};
class D :public B,public C
{
public:
	D(int data) :B(data),C(data), md(data) { cout << "D()" << endl; }
	~D() { cout << "~D()" << endl; }
private:
	int md;
};
int main()
{	
	D d(10);

	return 0;
}



通过将B和C类虚继承A类可以解决A的重复初始化。

class B:virtual public A;
class C:virtual public A;
D(int data) :A(data),B(data),C(data), md(data) 
//需要显示调用A的构造函数,要不然没有默认构造函数A就会出错



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++继承和多态 的相关文章

随机推荐

  • centos 6 yum源不可用安装报YumRepo Error: All mirror URLs are not using ftp, http[s] or file

    项目场景 centos6 5 使用yum安装资源时 报如下错误 1 YumRepo Error All mirror URLs are not using ftp http s or file 解决方案 修改 etc yum repos d
  • Spring Data 与MongoDB 集成四:操作篇(查询)

    本文转载至 http blog csdn net congcong68 article details 47183209 一 简介 spring Data MongoDB提供了org springframework data mongodb
  • 论文阅读:Improved Denoising Diffusion Probabilistic Models

    本文是对ddpm简单的修改 但是能提高ddpm的性能 论文下载地址 https proceedings mlr press v139 nichol21a html 我们发现反向过程中可学习的方差允许一个数量级的采样 样本质量的差异可以忽略不
  • AOP中的代理对象

    先要了解spring容器初始化过程中Bean的生命周期 如果spring在启动过程中加上了 Transiation注释的话 spring会生成一个代理对象 来做事务控制 我们从容器中取出来的对象是代理对象 代理对象在执行方法之前会开启事务管
  • 浏览器兼容性问题解决

    样式兼容性 css 方面 解决方法 可以通过 Normalize css 抹平差异 也可以定制自己的 reset css 全局重置样式 firefox浏览器不支持cursor hand显示手型 解决方法 统一使用cursor pointer
  • C# 用Microsoft.Office.Interop.PowerPoint类库操作PPT

    前言 最近由于项目需求 需要使用此类库对PPT进行操作 1 引用 Microsoft Office Interop PowerPoint和 Microsoft Office Core 2 PPT操作 打开PPT PPT应用程序变量 Appl
  • vue如何获取当前路径url及参数

    有时候开发需要获取当前url的参数 完整url可以用 window location href 路由路径可以用 this route path 路由路径参数 this route params params是参数名称 this route
  • Django-模型层(单表操作)

    目录 1 ORM简介 2 单表操作 2 1创建表 2 2添加表纪录 2 3查询表纪录 2 4删除表纪录 2 5修改表纪录 1 ORM简介 MVC或者MVC框架中包括一个重要的部分 就是ORM 它实现了数据模型与数据库的解耦 即数据模型的设计
  • 2019 前端框架对比及评测

    Jacek Schae 原作 授权 LeanCloud 翻译 我们将基于 RealWorld 示例应用对比前端框架 RealWorld 示例应用的特点 RealWorld 应用 比待办事项类应用更复杂 通常待办事项类应用不足以传达足够多的知
  • 最详细的Python接单思路和方法

    首先讲下python爬虫怎么接单挣钱 Python爬虫挣钱方法大致如下 有些需要的技术并不难 这种大部分都可以做 有些可能对技术要求比较高 门槛相对就高一点 爬虫技术挣钱方法1 接外包爬虫项目 这是网络爬虫最通常的的挣钱方式 通过外包网站
  • LeetCode-环形链表-简单

    标题 141环形链表 简单 题目 给你一个链表的头节点 head 判断链表中是否有环 如果链表中有某个节点 可以通过连续跟踪 next 指针再次到达 则链表中存在环 为了表示给定链表中的环 评测系统内部使用整数 pos 来表示链表尾连接到链
  • IDEA中配置Maven常见问题每次都需要更改setting设置,否则使用默认Maven,完美解决Maven的配置问题!

    废话不说 直接上图 你是否也遇到这种情况呢 创建maven无模板项目时 maven总是idea自带 如何解决呢 点开系统setting 1 取消默认打开上一次项目 2 重启IDEA 在全局设置就可以了 完美解决 创作不易 问题解决的给个鼓励
  • 520,我会处理回文数了,你会了么?(dp+中心扩散)

    给定一个字符串 s 找到 s 中最长的回文子串 你可以假设 s 的最大长度为 1000 示例 1 输入 babad 输出 bab 注意 aba 也是一个有效答案 示例 2 输入 cbbd 输出 bb 方法一 暴力匹配 Brute Force
  • git commit-m 与 git commit -a -m

    转自 https segmentfault com q 1010000005900988 字面解释的话 git commit m用于提交暂存区的文件 git commit am用于提交跟踪过的文件 要理解它们的区别 首先要明白git的文件状
  • python二级第一套答案

    python二级第一套答案 46 考生文件夹下存在3个Python源文件 分别对应3个问题 1个文本文件 作为本题目输入数据 请按照源文件内部说明修改代码 实现以下功能 命运 是著名科幻作家倪匡的作品 这里给出 命运 的一个网络版本文件 文
  • 华为笔记本转轴坏了修复指南记录

    华为笔记本转轴坏了修复指南记录 前言 记录一下 华为笔记本MeteBookD MRC W50 转轴破裂导致屏幕翻盖时 转轴出开裂 记录维修全过程 由于之前电脑坏掉的图片找不到了 我这里就先大致描述一下吧 看下图 上面我就是简单的描述了一些我
  • 行列式的英文翻译

    行列式的英文翻译 行列式的英文翻译是 determinant 行列式的英文定义为 从定义可以看出 只有方阵才会有行列式的定义
  • applicationContext.xml文件如何调用外部properties等配置文件

  • 记一次计算机20春招的过程

    今年秋招没有参加 因为考研 等到考研成绩出来 发现无缘复试 便开始准备春招 基本是从2月初就开始准备了 刚开始没有确定企业类型 胡乱投了一下 有百度大厂等 最后还是决定投银行 对于女生来说 还是比较稳定 主要投了中信银行 上海银行 苏州银行
  • C++继承和多态

    C 继承和多态 继承 继承的本质 代码的复用 在基类中给所有派生类提供统一的虚函数接口 让派生类进行重写 然后就可以使用多态了 类和类的关系 a part of 一部分关系 继承 a kind of 一种的关系 继承引入了一些概念 基类 父