6-继承

news2024/10/6 18:41:21

6-继承

  • 1、基本语法和方式
  • 2、继承的基本特点
    • 2.1 三种继承方式相同的基本点
    • 2.2 三种继承方式的差别
    • 2.3 公有继承的独有特点
  • 3、子类的构造、析构
    • 3.1 子类的构造
    • 3.2 子类的析构
    • 3.3 子类的拷贝构造函数
    • 3.4 子类的拷贝赋值
  • 4、多重继承
    • 4.1 内存布局
    • 4.2 类型转换
    • 4.3 名字冲突问题
  • 5、砖石继承
  • 6、虚继承

1、基本语法和方式

class 子类 : 继承方式1 基类1, 继承方式2 基类2, ... {
	...
};
  • 继承方式
    公有继承:public
    保护继承:protected
    私有继承:private

2、继承的基本特点

2.1 三种继承方式相同的基本点

  • 继承所要达到的目的:
    • 子类对象包含基类子对象
    • 子类内部可以直接访问基类的所有非私有成员
  • 继承的本质:
    • 基类的非私有成员在子类中仅仅为可见,而非拥有
      注意:
      对于继承切忌不要理解为基类的成员变为子类的成员,继承不会改变类成员的作用域,基类的成员永远都是基类的成员,并不会因为继承而变成子类的成员
  • 尽管基类的公有和保护成员在子类中直接可见,但仍然可以在子类中重新定义这些名字,子类中的名字会隐藏所有基类中的同名定义
  • 如果需要在子类内部访问 一个在基类中定义却被子类标识符所隐藏的名字,可以借助作用域限定操作符“::”实现
  • 因为作用域的不同,分别在子类和基类中定义的同名成员函数(包括静态成员函数),并不构成重载关系,相反是一种隐藏关系
  • 任何时候,在子类的内部,总可以通过作用域限定操作符“::”,显式地调用那些在基类中定义却被子类所隐藏的成员
// 继承最基本的特点
// (1) 子类对象的内部 包含 基类子对象
// (2) 子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)
// (3) 一旦被子类同名定义隐藏 可以借助::指明访问基类
class Base{
public:
	int m_a;
	void foo(){ cout << "Base::foo" << endl; }
protected:
	int m_b;
	void bar(){ cout << "Base::Bar" << endl; }
private:
	int m_c;
	void hum(){ cout << "Base::hum" << endl; }
};
class Derived :public Base{
public:
	void fun(){
		m_a = 100;
		Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foo
		m_b = 100;
		bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)
		//m_c = 100;
		//hum();
	}
private:
	int m_d;
	void foo(){ cout << "Derived::foo" << endl; }
	void bar(){ cout << "Derived::Bar" << endl; }
};
int main(){
	Base b;// 基类对象
	cout << "基类对象b的大小" << sizeof(b) << endl; // 12
	Derived d;// 子类对象
	cout << "子类对象d的大小" << sizeof(d) << endl; // 16  子类对象的内部 包含 基类子对象
	d.fun();
	return 0;
}

2.2 三种继承方式的差别

  • 基类中的公有、保护和私有成员,在子类中将对这些基类成员的访问控制限定进行重新标记
// public继承的独特点
class publicBase :public Base{// 子类将对基类的成员重新标记访问限定符
	// 子类将对基类的成员重新标记访控限定 m_a/foo是public m_b/bar是protected m_c/hum是private
public:
	void fun(){ // //子类内部访问基类成员时,编译器需要查看这些成员在 基类中的原始标记
		m_a = 100;
		Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foo
		m_b = 100;
		bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)
	}
private:
	int m_d;
};
// protected继承的独特点
class protectedBase :protected Base{// 子类将对基类的成员重新标记访问限定符
	// 子类将对基类的成员重新标记访控限定 m_a/foo是protected m_b/bar是protected m_c/hum是private
public:
	void fun(){ //子类内部访问基类成员时,编译器需要查看这些成员在 基类中的原始标记
		m_a = 100;
		Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foo
		m_b = 100;
		bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)
	}
private:
	int m_d;
};
// private继承的独特点
class privateBase :private Base{// 子类将对基类的成员重新标记访问限定符
	// 子类将对基类的成员重新标记访控限定 m_a/foo是private m_b/bar是private m_c/hum是private
public:
	void fun(){ //子类内部访问基类成员时,编译器需要查看这些成员在 基类中的原始标记
		m_a = 100;
		Base::foo();// 子类的foo函数将基类的foo函数隐藏,但可以通过作用域限定符访问基类的foo
		m_b = 100;
		bar();  //子类内部可以直接访问 基类的 非私有(共有/保护)成员(变量/函数)
	}
private:
	int m_d;
};
int main(){
	publicBase d;// 利用子类对象在类外访问 基类成员时,编译器需要查看这些成员在 子类中的重新标记
	d.m_a = 10;
	d.foo();
	//d.m_b = 10; // err
	//d.bar();// err
	//d.m_c = 30;// err
	//d.hum();// err
	protectedBase b;// 利用子类对象在类外访问 基类成员时,编译器需要查看这些成员在 子类中的重新标记
	//b.m_a = 10;// err
	//b.foo();// err
	//b.m_b = 10; // err
	//b.bar();// err
	//b.m_c = 30;// err
	//b.hum();// err
	privateBase cd;// 利用子类对象在类外访问 基类成员时,编译器需要查看这些成员在 子类中的重新标记
	//cd.m_a = 10;// err
	//cd.foo();// err
	//cd.m_b = 10; // err
	//cd.bar();// err
	//cd.m_c = 30;// err
	//cd.hum();// err
	return 0;
}
基类中的在公有子类中标记为在保护子类中标记为在私有子类中标记为
公有成员公有成员保护成员私有成员
保护成员保护成员保护成员私有成员
私有成员私有成员私有成员私有成员
  • “通过”子类访问其所继承的基类的成员时,需要考虑因继承方式对访问控制限定的影响

2.3 公有继承的独有特点

(1) 只有在公有继承下,子类对象在类外可以访问基类的 公有成员(其他继承不可以)
(2) 如果被子类同名标识符隐藏可以借助::指明访问基类的成员
(3) 只有在公有继承下 子类类型指针 和 基类类型指针 之间可以进行转换
子类类型引用 和 基类类型引用 之间可以进行转换

class publicBase :public Base{
public:
	void foo(){
		cout << "publicBase::foo" << endl;
	}
private:
	int m_d;
};
class Human{
public:
	int m_d;
	string m_name;
};
class Student :public Human{
public :
	int m_no;
};
int main(){
	publicBase b;
	b.m_a = 1000;
	b.foo(); // 调用的是自己的
	b.Base::foo(); // 调用的是基类的
	Human h;
	cout << "基类对象h的大小" << sizeof(h) << endl;
	Student s;
	cout << "子类对象s的大小" << sizeof(s) << endl;
	// 子类类型指针 和 基类类型指针
	Human* ph = &s; // Student* --> Human*
	// 子类类型引用 和 基类类型引用
	Human& rh = s;
	// 以上两种转换,编译器认为访问范围缩小,是安全的
	//Student* ps = &h;
	//Student& rs = h;
	// 以上两种转换,编译器认为访问范围扩大,是危险的
	Student* ps = static_cast<Student*> (&h);
	Student& rs = static_cast<Student&>(h);
	// 通过静态转换虽然可以成功,但是风险依旧存在,极其不建议这么使用
	Student* ps = static_cast<Student*>(ph);// ph 指向的就是C6_Student的对象
	Student& rs = static_cast<Student&>(rh);// rh 就是C6_Student对象的引用
	// 以上两种转换毫无风险,极其建议大家这么做
	// 基类指针或引用的实际目标,究竟是不是子类对象,完全由我们自己判断
	return 0;
}

向上造型:

  • 子类类型指针 隐式 转换为基类类型指针
  • 子类类型引用 隐式 转换为基类类型引用

3、子类的构造、析构

3.1 子类的构造

  • 子类没有定义构造函数
    • 编译器为子类提供的默认无参构造函数,定义基类子对象,并调用其基类的无参构造函数,构造该子类对象中的基类子对象。
  • 子类定义构造函数但没有在初始化表中指明基类部分构造方式
    • 定义基类子对象,并调用其基类的无参构造函数,构造该子类对象中的基类子对象。
  • 子类定义构造函数并在初始化表中指明基类部分构造方式
    • 定义基类子对象并 调用指明的其基类的构造函数。
  • 子类对象的构造过程
    • 构造基类子对象->构造成员变量->执行构造代码

3.2 子类的析构

  • 子类没有定义析构函数
    • 编译器将提供一个默认析构函数,析构完所有的成员变量以后,会自动调用其基类的析构函数.
  • 子类定义析构函数
    • 子类的析构函数在执行完自身析构代码,并析构完所有的成员变量以后,会自动调用其基类的析构函数.
  • 子类对象的析构过程
    • 执行析构代码->析构成员变量->析构基类子对象

3.3 子类的拷贝构造函数

  • 子类没有定义拷贝构造函数
    • 编译器为子类提供的默认拷贝构造函数,定义基类子对象,并调用其基类的拷贝构造函数构造该子类对象中的基类子对象
  • 子类定义了拷贝构造函数,但没有在初始化表指明其基类部分的构造方式
    • 定义基类子对象,并调用其基类的无参构造函数,构造该子类对象中的基类子对象
  • 子类定义了拷贝构造函数,同时初始化表中指明了其基类部分以拷贝方式构造
    • 定义基类子类对象,并调用其基类的拷贝构造函数,构造该子类对象中的基类子对象

3.4 子类的拷贝赋值

  • 子类没有定义拷贝赋值函数
    • 编译器为子类提供的缺省拷贝赋值函数,会自动调用其基类的拷贝赋值函数,复制该子类对象中的基类子对象
  • 子类定义了拷贝赋值函数,但没有显式调用其基类的拷贝赋值函数
    • 子类对象中的基类子对象将得不到复制
  • 子类定义了拷贝赋值函数,同时显式调用了其基类的拷贝赋值函数
    • 子类对象中的基类子对象将得到复制
class C6_people{
public:
	C6_people(int age = 0, const char*name = "无名"):m_age(age), m_name(name){
		//【int m age=age;】定义mage,初值为age
		//【string m name(name);】定义m name,利用m name,string(name)
		cout << "people类的缺省构造函数被调用" << endl;
	}
	C6_people(const C6_people&that):m_age(that.m_age),m_name(that.m_name){
		// 【int m_age = that, mage; 】定义mage, 初值为that.m age
		//【string m name=that.m name;】定义m name,利用m name.string(that.m name)
		cout << "people类的拷贝构造函数被调用" << endl;
	}
	C6_people&operator=(const C6_people& that){
		//编译器不会在自定义拷贝赋值函数中塞任何操作
		cout << "people类的拷贝赋值函数被调用" << endl;
		this->m_age=that.m_age;
		this->m_name=that.m_name; // this->m name.operator=(that.m name)
		return *this;
	}
	~C6_people(){
		cout << "C6_people类的析构函数被调用" << endl;
		//对于基本类型的成员变量m_age,什么都不做
		//对于类类型的成员变量m_name,利用m_name,~string()
		//释放m_age/m_name本身所占内存空间
	}
	void getinfo(){
		cout << "姓名:" << m_name << ",年龄:" << m_age ;
	}
private:
	int m_age;// 基本类型成员变量
	string m_name;// 类类型成员变量
};
class C6_stu : public C6_people{
public:
	//子类没有定义构造函数编译器为子类提供的默认无参构造函数
	//C6_stu(){
	//	【Human(); 】定义基类子对象,利用基类子对象.Human()
	//  【float m score; 】
	//	【string m remark; 】
	//}
	C6_stu(int age = 45, string name = "张三", float score = 0.0, string remark = "优秀") :m_score(score), m_remark(remark), C6_people(age,name.c_str()){
		cout << "基类的缺省构造被调用了" << endl;
	}
	void getinfo(){
		C6_people::getinfo();
		cout << "分数:" << m_score << "评语:" << m_remark << endl;
	}
	//子类没有定义析构函数编译器将提供一个默认析构函数
	//~C6_stu(){
	//对于基本类型的成员变量m_score,什么都不做
	//对于类类型的成员变量m m_remark,利用m_remark,~string()
	// 对于基类子对象,利用基类子对象.~C6_people()
	//释放m_score/m_remark本身所占内存空间
	//}
	~C6_stu(){
		cout << "C6_stu类的析构函数被调用" << endl;
	}
	//子类没有定义拷贝构造函数编译器为子类提供的默认拷贝构造函数
	//C6_stu(const C6_stu& that){
	//	// [C6_people(that)] 定义基类子对象,利用基类子对象.C6_people(that) ->C6_people类拷贝构造函数
	//	// ...
	//}
	// 子类没有定义拷贝赋值函数编译器为子类提供的缺省拷贝赋值函数
	//C6_stu& operator=(const C6_stu& that){
	//	C6_people *p = this;
	//	*p = that;// C6_people类的拷贝赋值函数
	//}

private:
	float m_score;
	string m_remark;
};
int main(){
	cout << "----s1" << endl;
	C6_stu s1(22,"李四",89.7,"良好");// 构造
	s1.getinfo();
	cout << "----s2" << endl;
	C6_stu s2 = s1; // 拷贝构造
	s2.getinfo();
	cout << "----s3" << endl;
	C6_stu s3;
	cout << "s3赋值前" << endl;
	s3.getinfo();
	s3 = s2; // 赋值拷贝
	cout << "s3赋值后" << endl;
	s3.getinfo();
	return 0;
}

4、多重继承

4.1 内存布局

  • 子类对象中的多个基类子对象,按照继承表的顺序依次被构造,析构的顺序则与构造严格相反,各个基类子对象按照从低地址到高地址排列
    在这里插入图片描述
class C06_A{
public:
	int m_a;
	C06_A(){ cout << "C06_A构造" << endl; }
	~C06_A(){ cout << "C06_A析构" << endl; }
};
class C06_B{
public:
	int m_b;
	C06_B(){ cout << "C06_B构造" << endl; }
	~C06_B(){ cout << "C06_B析构" << endl; }
};
class C06_C{
public:
	int m_c;
	C06_C(){ cout << "C06_C构造" << endl; }
	~C06_C(){ cout << "C06_C析构" << endl; }
};
class C06_D :public C06_A, public C06_B, public C06_C{ // 汇聚子类
public:
	int m_d;
	C06_D(){ cout << "C06_D构造" << endl; }
	~C06_D(){ cout << "C06_D析构" << endl; }
};
int main(){
	C06_D d;
	cout << "汇聚子类对象d的大小:" << sizeof(d) << endl;
	C06_D * pd = &d;
	cout << "整个汇聚子类对象的首地址D* pd:" << pd << endl;
	cout<< "A基类子对象的首地址:" << &d.m_a<<endl;
	cout << "B基类子对象的首地址:" << &d.m_b << endl;
	cout << "C基类子对象的首地址:"<<&d.m_c<< endl;
	cout << "D基类子对象的首地址:" << &d.m_d << endl;
	return 0;
}

4.2 类型转换

  • 将多重继承的子类对象的指针,隐式转换为它的基类类型,编译器会根据各个基类子对象在子类对象中的内存位置,进行适当的偏移计算
  • 反之,将任何一个基类类型的指针静态转换为子类类型,编译器同样会进行适当的偏移计算
  • 无论在哪个方向上,重解释类型转换(reinterpret_cast)都不进行任何偏移计算–不要使用
    在这里插入图片描述
int main(){
	C06_D d;
	cout << "汇聚子类对象d的大小:" << sizeof(d) << endl;
	C06_D * pd = &d;
	cout << "整个汇聚子类对象的首地址D* pd:" << pd << endl;// 004FF730
	cout << "A基类子对象的首地址:" << &d.m_a << endl; // 004FF730
	cout << "B基类子对象的首地址:" << &d.m_b << endl; // 004FF734
	cout << "C基类子对象的首地址:" << &d.m_c << endl; // 004FF738
	cout << "D基类子对象的首地址:" << &d.m_d << endl; // 004FF73C
	C06_A* pa = pd;
	cout << "隐式转换" << endl;
	cout << "D* pd ---> A* pa:" << pa << endl; // 004FF730
	C06_B* pb = pd;
	cout << "D* pb ---> B* pb:" << pb << endl; // 004FF734
	C06_C* pc = pd;
	cout << "D* pd ---> C* pc:" << pc << endl; // 004FF738
	cout << "静态转换" << endl;
	C06_D* p1 = static_cast<C06_D*>(pa);
	cout << "A* pd ---> D* p1:" << p1 << endl; // 004FF730
	C06_D* p2 = static_cast<C06_D*>(pb);
	cout << "B* pb ---> D* p2:" << p2 << endl; // 004FF730
	C06_D* p3 = static_cast<C06_D*>(pc);
	cout << "C* pc ---> D* p3:" << p3 << endl; // 004FF730
	return 0;
}
  • 引用的情况与指针类似,因为引用的本质就是指针
    在这里插入图片描述

4.3 名字冲突问题

  • 如果在子类的多个基类中,存在同名的标识符,那么任何试图通过子类对象,或在子类内部访问该名字的操作,都将引发歧义。
    名字冲突问题解决方法
  • 子类隐藏该标识符–不建议使用
  • 通过作用域限定操作符“::”显式指明所属基类
class C06_E{
public:
	int m_a;
	int m_c;
};
class C06_F{
public:
	int m_b;
	int m_c;
};
class C06_G :public C06_E, public C06_F{
public:
	int m_d;
	void foo(){
		// m_c = 100;歧义
		C06_E::m_c = 100;
	}
};
int main(){
	C06_G g;
	cout << "汇聚子类对象g的大小:" << sizeof(g) << endl;
	// g.m_c=100; 歧义
	g.C06_E::m_c = 100;
	return 0;
}

5、砖石继承

  • 砖石继承的问题
    一个子类继承自多个基类,而这些基类又源自共同的祖先,这样的继承结构称为钻石继承(菱形继承)
    在这里插入图片描述
class C06_AA{ // 公共基类
public:
	int m_a;
};
class C06_X :public C06_AA{ // 中间子类
public:
	int m_x;
};
class C06_Y :public C06_AA{ // 中间子类
public:
	int m_y;
};
class C06_Z :public C06_X, public C06_Y{ // 汇聚子类
public:
	int m_z;
};
int main(){
	C06_Z z; // X中间子类子对象|Y中间子类子对象|m_z|-->
			// A公共基类子对象 m_x|A公共基类子对象 m_y|m_z|-->
			// m_a m_x |m_a m_y|m_z|
	cout << "汇聚子类对象g的大小:" << sizeof(z) << endl;// 20
	return 0;
}
  • 钻石继承问题
    在汇聚子类内部,或通过汇聚子类对象,访问公共基类的成员,会因继承路径的不同而导致匹配歧义

6、虚继承

解决砖石继承存在的问题而诞生的

  • 在继承表中使用virtual关键字

  • 虚继承可以保证
    (1)公共虚基类子对象在汇聚子类对象中仅存一份实例
    在这里插入图片描述

    (2)公共虚基类子对象被多个中间子类子对象所共享
    虚继承实现原理

  • 汇聚子类对象中的每个中间子类子对象都持有一个指针,通过该指针可以获取中间子类子对象的首地址公共虚基类子对象的首地址的偏移量
    在这里插入图片描述

class C06_AA{ // 公共基类
public:
	int m_a;
};
class C06_X :virtual public C06_AA{ // 中间子类
public:
	int m_x;
	void setAge(/* X* this */int age){
		m_a = age; // this -> X 中间子类子对象-》指针1-》偏移量-》this+偏移量-》A公共基类子对象-》m_a
	}
	
};
class C06_Y :virtual public C06_AA{ // 中间子类
public:
	int m_y;
	int getAge(/* Y* this*/){ 
		return m_a;// this->Y 中间子类子对象 - 》指针2 - 》偏移量 - 》this + 偏移量 - 》A公共基类子对象 - 》m_a
	}
};
class C06_Z :public C06_X, public C06_Y{ // 汇聚子类
public:
	int m_z;
};
int main(){
	C06_Z z; // X中间子类子对象|Y中间子类子对象|m_z|A公共基类子对象|-->
			// 指针1 m_x |指针2 m_y|m_z|m_a|
	cout << "汇聚子类对象g的大小:" << sizeof(z) << endl;// 32
	z.setAge(100); // setAge(&z) -->实参类型Z*
	cout << "age:" << z.getAge() << endl; // getAge(&z) -->实参类型Z*
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1710810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EtherCAT协议概述

EtherCAT 是由德国 BECKOFF 自动化公司于2003年提出的实时工业以太网技术。它具有高速和高数据有效率的特点&#xff0c;支持多种设备连接拓扑结构。其从站节点使用专用的控制芯片&#xff0c;主站使用标准的以太网控制器。 EtherCAT 的主要特点如下 : ①泛的适用性&#xff0c…

Python3 笔记:Python的turtle库

Python的turtle库是一个用于绘制图形的库。 举例&#xff1a; import turtle turtle.bgcolor(white) turtle.width(2) sides5 colors["red","yellow","blue","orange","green","purple"] for x in range(60):tu…

基于双PI结构FOC闭环控制的永磁同步电机控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于双PI结构FOC闭环控制的永磁同步电机控制系统simulink建模与仿真。 2.系统仿真结果 3.核心程序与模型 版本&#xff1a;MATLAB2022a 64 4.系统原理简介 永磁同步电机&a…

YoloV9改进策略:卷积篇|基于PConv的二次创新|附结构图|性能和精度得到大幅度提高(独家原创)

文章目录 摘要论文指导PConv在论文中的描述改进YoloV9的描述改进代码与结构图改进方法测试结果总结摘要 在PConv的基础上做了二次创新,创新后的模型不仅在精度和速度上有了质的提升,还可以支持Stride为2的降采样。 改进方法简单高效,需要发论文的同学不要错过! 论文指导 …

vue页面滚动条触底事件

html 滚动区域外层div添加属性 ref&#xff0c;id&#xff0c;scroll 在mounted函数中使用document设置监控div属性 在mathods中监控滚动事件

Go Redis 实现邮件群发

一、安装 go get github.com/go-redis/redis/v8二、邮箱服务配置&#xff0c;以QQ邮箱为例 三、示例代码 package mainimport ("context""fmt"redis "github.com/go-redis/redis/v8""gopkg.in/gomail.v2""gopkg.in/ini.v1&qu…

让ctexbook章节首页显示页眉

使用ctexbook构建的latex版本的学位论文或者其他用途, 章节的首页不显示页眉&#xff0c;如下图&#xff1a; 如果说&#xff0c;想要在章节的首页设置页眉&#xff0c;该如何设置&#xff1f; \usepackage{fancyhdr} \fancyhf{} \chead{暨南大学硕士学位论文} \cfoot{\thepage…

设计模式:装饰模式(Decorator)

设计模式&#xff1a;装饰模式&#xff08;Decorator&#xff09; 设计模式&#xff1a;装饰模式&#xff08;Decorator&#xff09;模式动机模式定义模式结构时序图模式实现在单线程环境下的测试在多线程环境下的测试模式分析优缺点适用场景应用场景应用实例模式扩展参考 设计…

抖店起店玩法,2024年最新保姆级抖音小店开店教程

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89360739 更多资源下载&#xff1a;关注我。 课程内容&#xff1a; 1-抖音如何精细化选品 2-达人合作的谈判技巧 3-达人合作细节注意事项 4-短视频达人筛选方法与数据维度 5-短视频带货达人分析工具…

XDebug配置极简教程,phpstorm实现http请求断点调试

写这篇的文章的初衷:网络上配置XDebug的文章有很多,XDebug也有官方的文档, PhpStorm也有官方的文档,为什么还要写那? 相信不少人,都有一种感觉,虽然教程很多,但是按教程走一遍,自己的确不能正常调试。 问题出在下面几个方面: 1. 对调试过程中,没有一定的认识,因此…

模板详解:泛型、函数\类模板、特化

文章目录 泛型编程函数模板概念格式原理实例化参数匹配原则 类模板格式实例化 非类型模板参数模板的特化函数模板特化类模板特化全特化偏特化 模板分离编译模板总结 泛型编程 函数重载的缺点&#xff1a; 重载的函数仅仅是类型不同&#xff0c;代码复用率比较低&#xff0c;只…

如何批量提取pdf文件名?批量提取文件夹里的文件名,只要用对方法!

在数字化时代&#xff0c;PDF文件已经成为我们日常工作中不可或缺的一部分。然而&#xff0c;随着PDF文件数量的不断增加&#xff0c;如何高效地管理这些文件成为了一个挑战。批量提取PDF文件名&#xff0c;就是解决这一问题的关键所在。本文将为你介绍几种实用的方法&#xff…

【Qt秘籍】[002]-开始你的Qt之旅-下载

一、Qt的开发工具有哪些&#xff1f; Qt的开发工具概述Qt支持多种开发工具&#xff0c;其中最常见的开发工具是 1.QtCreator 【易上手/有少量bug/适合新手】 2.VisualStudio 【功能强大/易出错/需要更多额外配置】 3.Eclipse 【清朝老兵IDE/不建议使用】 【注意&#xff1…

Apache Impala 4.4.0正式发布了!

历时半年多&#xff0c;Impala 4.4终于发布了&#xff01;本次更新带来了不少新功能&#xff0c;受限于篇幅&#xff0c;这里简要列举一些&#xff0c;后续文章再挑重点的进行介绍。 支持更多Iceberg表上的语句 支持对 Iceberg V2 表的 UPDATE 语句&#xff0c;用来更新已有数…

已解决ModuleNotFoundError : No module named ‘pandas亲测有效!!!

已解决ModuleNotFoundError : No module named ‘pandas亲测有效&#xff01;&#xff01;&#xff01; 亲测有效 报错问题解决思路解决方法 报错问题 在运行Python代码时&#xff0c;你可能会遇到以下报错信息&#xff1a; ModuleNotFoundError: No module named pandas这个…

计算机组成原理----原码,反码与补码

目录 1.原码的出现 2.反码的出现 3.补码的出现 4.关于补码 1.原码的出现 我们通常使用"","-"判断数字的正负,而在计算机中,则将二进制的首位当作标记符号,"0"表示正数,"1"表示负数,这样就解决了在计算机中表示数值正负的问题,唯一…

剖析【C++】——类与对象(中)——小白篇—超详解

目录 1.类的6个默认成员函数&#xff1a; 1. 默认构造函数&#xff08;Default Constructor&#xff09; 2. 析构函数&#xff08;Destructor&#xff09; 3. 拷贝构造函数&#xff08;Copy Constructor&#xff09; 4. 拷贝赋值运算符&#xff08;Copy Assignment Operato…

【Mybatis】映射文件中#{}里的其他属性

#{}里除了可以写接收参数的名称外&#xff0c;还可以设置javaType&#xff0c;jdbcType&#xff0c;mode&#xff0c;numericScale,resultMap,typeHandler,jdbcTypeName这些属性的。 其他的属性除mode&#xff08;存储过程相关讲到存储过程的时候在讲&#xff09;外使用方式都…

单链表的相关题目

1.删除链表中给定值val的所有结点 public void removeall(int key) {//由于是删除链表中所有和key值相同的结点,所以可以设置两个ListNode类型的数据,一个在前面,一个在后面.//直到前面的走到链表的最后,这样完成了遍历.//先判断一下这个链表是否为空if(headnull){System.out.…

Vim安装与配置教程(解决软件包Vim没有安装可候选)

一、Vim检测是否安装 1-输入vi查看是否安装&#xff1b; 2-按Tab键&#xff0c;显示以下字符为未安装&#xff1b; 3-显示以下字符为已安装&#xff08;可以看到有Vim&#xff09; 二、Vim安装过程 1. 打开终端&#xff0c;输入 sudo apt install vim; 2. 输入Y/y&#xff…