“码” 上认亲!面向对象之继承:解锁代码的 “家族传承” 密码

bartender 10.1破解版|bartender条码打印10.1 点击下载出处:BarTender10.1破解版 bartender10.1破解版是一款专业的条码打印软件,在行业内广受好评,通过使用bartender我们可以将标签设计这个复杂的过程变得更简便,在设计中快速调整,即刻打印,非常迅速,BarTender10.1破解版不仅支持的标签类型包括EPC Class 1 、Gen2 、ISO 18000-6b 、ISO 15693 、Tag-It 、I-CO... 阅读详情

个人主页

🎬 个人主页:Vect个人主页

🎬 GitHub:Vect的代码仓库

🔥 个人专栏: 《数据结构与算法》《C++学习之旅》《计算机基础》

⛺️Per aspera ad astra.


1.继承基础(公有继承基础下实现)

继承定义

在实现一个系统时,我们会经常遇到具有类似属性,但细节或行为存在细微差异的组件 。比如说外卖配送系统:最重要的三个角色:骑手、客户和商家,他们有共同的属性:地址、联系方式等等,也有每个类个性化的属性。

这种情况有两种解决方式:

  • 将每个组件声明位一个类,并在每个类中实现所有的属性,这会重复实现相同的属性
  • 继承: 从一个包含通用属性并且实现了通用功能的基类派生出类似的类,并在类中覆盖基本功能,来实现让每个类都有独一无二的功能。

这便是面向对象的第二大特性:继承

如图:类之间的继承关系

在这里插入图片描述

根据上述介绍,可以总结出继承的定义:

继承是面向对象程序设计使代码可以复用的手段,允许设计者在保证原有类特性的基础上进行扩展,增加新的功能,产生新的类。继承呈现了面向对象程序设计的层次结构

继承语法

在这里插入图片描述

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

// 鱼品种的基类
class Fish {
protected:
	bool isFreshWaterFish;
public:
	void swim() {
		if (isFreshWaterFish) cout << "在湖泊小河中生存的鱼" << endl;
		else cout << " 在海洋中生存的鱼" << endl;
	}
};

class Carp : public Fish {
public:
	Carp() { isFreshWaterFish = true; }
};

class Tuna : public Fish {
public:
	Tuna() { isFreshWaterFish = false; }
};

int main() {
	Carp myLunch;
	Tuna myDinner;

	cout << "我的食物:" << endl;
	cout << "午餐:";
	myLunch.swim();
	cout << "晚餐:";
	myDinner.swim();

	return 0;
}

注意protected这个限定符出现在基类中,它的意思是保护isFreshWaterFish只能在继承层次结构体系中访问和使用,而若没有protected,则能在继承层次结构体系外部访问和使用。

基类初始化——向基类传递参数

如果基类包含重载的构造函数,需要在实例化时给它提供实参,该如何办呢?创建派生对象时将如何实例化这样的基类?方法是使用初始化列表,并通过派生类的构造函数调用合适的基类构造函数,代码如下所示:

class Base {
public:
	Base(int someNumber){
		//...
	}
};
class Derived : public Base {
public:
	Derived()
		:Base(5) // 用基类走初始化列表
	{ }
};

对于我们定义的Fish类,通过给Fish的构造函数提供一个布尔值,来初始化Fish::isFreshWaterFish,强制每个派生类指出自己的品种,代码如下:

class Fish {
protected:
	bool isFreshWaterFish;
public:
	Fish(bool isFreshWaterFish)
		: isFreshWaterFish(isFreshWaterFish)
	{ }

	void swim() {
		if (isFreshWaterFish) cout << "在湖泊小河中生存的鱼" << endl;
		else cout << " 在海洋中生存的鱼" << endl;
	}
};

class Carp : public Fish {
public:
	Carp()
		:Fish(true)
	{ }
};

class Tuna : public Fish {
public:
	Tuna()
		:Fish(false)
	{ }
};

int main() {
	Carp myLunch;
	Tuna myDinner;

	cout << "我的食物:" << endl;
	cout << "午餐:";
	myLunch.swim();
	cout << "晚餐:";
	myDinner.swim();

	return 0;
}

Fish有一个构造函数,接受一个参数用于初始化Fish::isFreshWaterFish。因此,要创建Fish对象,必须提供一个用于初始化该保护成员的参数,这样Fish避免了保护成员包含随机值的情况,派生类Carp和Tuna被迫定义一个构造函数,使用合适的参数来实例化基类Fish

在派生类中覆盖基类的方法

派生类用完全相同的函数签名重写基类的函数,相当于覆盖了基类的这个函数,

如下展示:

// 在派生类中覆盖基类的方法
class Base {
public:
	void func() {
		// ...
	}
};
class Derived : public Base {
public:
	void func() {
		// ...
	}
};

如果使用Derived类的实例化对象调用Func,调用不是Base类中的Func

我们换一组新的继承层次关系演示:

class Livestock {
protected:
    bool isWoolProducer;
public:
    Livestock(bool isWoolProducer)
        : isWoolProducer(isWoolProducer)
    {
    }

    void introduce() {
        if (isWoolProducer) cout << "以羊毛为主要产出的畜牧动物" << endl;
        else cout << "以奶/肉为主要产出的畜牧动物" << endl;
    }
};

class Cow : public Livestock {
public:
    Cow()
        : Livestock(false) 
    { }
    void introduce() {
        cout << "体重基数大,肉质紧实" << endl;
    }
};

class Sheep : public Livestock {
public:
    Sheep()
        : Livestock(true)  
    { }
    void introduce() {
        cout << "体重基数小,肉质软嫩" << endl;
    }
};

int main() {
    Cow myLunch;
    Sheep myDinner;

    cout << "我的食物:" << endl;
    cout << "午餐:";
    myLunch.introduce();
    cout << "晚餐:";
    myDinner.introduce();

    return 0;
}

Sheep::introduce覆盖了Livestock::introduce,想要调用Livestock::introduce,只能在main()中使用域作用做限定符显式调用

在派生类中调用基类

// 在派生类中调用基类的方法 ::显式调用
class Livestock {
protected:
    bool isWoolProducer;
public:
    Livestock(bool isWoolProducer)
        : isWoolProducer(isWoolProducer)
    {
    }

    void introduce() {
        if (isWoolProducer) cout << "以羊毛为主要产出的畜牧动物" << endl;
        else cout << " 以奶/肉为主要产出的畜牧动物" << endl;
    }
};

class Cow : public Livestock {
public:
    Cow()
        : Livestock(false) 
    { }
    void introduce() {
        cout << " 体重基数大,肉质紧实" << endl;
    }
};

class Sheep : public Livestock {
public:
    Sheep()
        : Livestock(true)  
    { }
    void introduce() {
        cout << "体重基数小,肉质软嫩" << endl;
    }
};

int main() {
    Cow myLunch;
    Sheep myDinner;

    cout << "我的食物:" << endl;
    cout << "午餐:";
    myLunch.Livestock::introduce();
    cout << "晚餐:";
    myDinner.Livestock::introduce();

    return 0;
}

在派生类中隐藏基类

覆盖的一种极端情况是隐藏

派生类里出现了同名成员(变量或函数),会把基类同名成员全部挡住——不论参数是否相同。这是名字查找规则导致的,属于编译期静态绑定问题

注意:隐藏只要是同名成员即是隐藏,这里函数隐藏和函数重载一定要区分开

  • 函数隐藏:出现在继承层次结构中,基类和派生类出现同名函数,派生类会隐藏基类的函数
  • 函数重载:同一作用域里,同名但参数列表不同的一组函数
// 隐藏:派生类中出现和基类同名的成员 隐藏基类成员
class Livestock {
protected:
    bool isWoolProducer;
public:
    Livestock(bool isWoolProducer)
        : isWoolProducer(isWoolProducer)
    {
    }


    void introduce(bool isWoolProducer) {
        if (isWoolProducer) cout << "以羊毛为主要产出的畜牧动物" << endl;
        else cout << " 以奶/肉为主要产出的畜牧动物" << endl;
    }
    void introduece() { cout << "......" << endl; }
};

class Cow : public Livestock {
public:
    Cow()
        : Livestock(false) 
    { }
    void introduce() {
        cout << " 体重基数大,肉质紧实" << endl;
    }
};

int main() {
    Cow myLunch;

    cout << "我的食物:" << endl;
    cout << "午餐:";
    myLunch.introduce();
    // error C2660: “Cow::introduce”: 函数不接受 1 个参数
    // myLunch.introduce(false);
    return 0;
}

注意看:这个版本Livestock实现了两个introduce的重载,Cow也实现了introduce,将基类的两个重载都隐藏了,如果取消注释36行,会发生编译报错。

那么,如何调用这两个重载呢?

  • 在main()中使用域作用限定符

myLunch.Livestock::introduce();

  • 在Cow类中,将Livestock的introduce
class Cow : public Livestock {
public:
    using Livestock::introduce;
    Cow()
        : Livestock(false) 
    { }
    void introduce() {
        cout << " 体重基数大,肉质紧实" << endl;
    }
};

2. 派生类中默认成员函数的行为

构造和析构

Cow是从Livestock派生而来的,创建Cow对象时,先调用Cow的构造函数还是Livestock的构造函数?实例化对象时,成员属性(Livestock::isWoolProducer)是调用构造函数之前实例化还是之后实例化?

基类对象在派生对象之前被实例化

  • 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。若基类没有默认构造函数,则必须再派生类的构造函数初始化列表显式调用
  • 派生类对象初始化先调用基类的构造再调用派生类构造
  • 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员,保证先清理派生类成员再清理基类成员
  • 派生类对象析构清理先调用派生类析构再调用基类析构

拷贝构造和赋值重载

  • 派生类的拷贝构造必须调用基类的拷贝构造完成基类的拷贝初始化
  • 派生类的operator=必须要调用基类的operator=完成基类的复制

具体行为演示

// 派生类成员函数行为

// 打印名字,观察顺序
struct Tracker {
	string _name;
	Tracker(string n)
		:_name(std::move(n)) {
		cout << " 构造 " << _name << endl;
	}
	~Tracker() { cout << " 析构 " << _name << endl; }
};

class Livestock {
protected:
	bool _isWoolProducer;
	int _age;
	Tracker _baseInfo;

	Livestock(bool w,int age) 
		:_isWoolProducer(w) 
		,_age(age)
		,_baseInfo("Livestock.baseInfo")
	{
		cout << "Livestock构造" << endl;
	}

	Livestock(const Livestock& other) 
		:_isWoolProducer(other._isWoolProducer)
		,_age(other._age)
		,_baseInfo(other._baseInfo)
	{
		cout << "Livestock拷贝构造" << endl;
	}
	~Livestock() { cout << "Livestock析构" << endl; }

	Livestock& operator=(const Livestock& copy) {
		if (this != &copy) {
			_isWoolProducer = copy._isWoolProducer;
			_age = copy._age;
			_baseInfo = copy._baseInfo;
			cout << "Livestock赋值重载" << endl;
		}
		return* this;
	}

	void introduce(bool wool) {
		if (wool) cout << "已羊毛为主要产出的畜牧动物" << endl;
		else cout << "以奶/肉为主要产出的畜牧动物" << endl;
	}

	void introduce() { cout << "......" << endl; }

};

class Cow : public Livestock {
private:
	int _weight;
	Tracker _cowInfo;
	const int _ID;
	const int& _ageAlias;
	
	static int weightInitial(int baseAge) { return 400 + baseAge * 10; } // 瞎编的

public:
	Cow()
		:Livestock(false, 3)			// 构造基类子对象 永远在最先
		, _ID(1234)						// cosnt成员必须在初始化列表赋值
		, _cowInfo("Cow._cowInfo")		// 根据声明顺序走初始化列表
		, _ageAlias(_age)				// 用基类的_age 基类已经构造好 安全
		, _weight(weightInitial(_age))
	{
		cout << "Cow 构造" << endl;
		cout << "-->weight=" << _weight
			<< ",age=" << _age
			<< ",ID=" << _ID << endl;
	}

	Cow(const Cow& other)
		:Livestock(other)		 // 先拷贝构造基类子对象(按基类列表顺序)
		, _weight(other._weight)
		, _cowInfo(other._cowInfo)
		, _ID(other._ID)
		, _ageAlias(_age)          // 绑定自己本体的 _age 而不是 other._ageAlias
	{
		cout << "Cow拷贝构造" << endl;
	}

	Cow& operator=(const Cow& copy) {
		if (this != &copy) {
			Livestock::operator=(copy);  // 先赋基类子对象
			_weight = copy._weight;
			_cowInfo = copy._cowInfo;
			// _ID 是 const:不可赋值(保持原值)
			// _ageAlias 是引用:不可重新绑定(仍引用“本对象的 _age”)
			cout << "Cow赋值重载" << endl;
		}
		return *this;
	}

	~Cow() { cout << "Cow析构" << endl; }

	using Livestock::introduce;
	void introduce() { cout << "体重基数大,肉质紧实" << endl; }

};

int main() {
	cout << "=== 构造 ===" << endl;
	Cow myLunch;
	cout << "午餐:" << endl;
	myLunch.introduce(); // 调用Cow无参版本
	cout << "午餐(基类重载):" << endl;
	myLunch.introduce(false); // 有using 可以访问到基类


	cout << "=== 拷贝构造 ===" << endl;
	Cow copyLunch = myLunch;   // 调用 Cow(const Cow&)

	cout << "=== 拷贝赋值 ===" << endl;
	Cow assigned;              // 先默认构造一个
	assigned = myLunch;        // 调用 Cow::operator=(const Cow&)

	cout << "=== 析构 ===" << endl;
	return 0;

}

默认构造调用顺序

  1. 先构造基类子对象:调用 Livestock(false, 3);其成员按声明顺序初始化:_isWoolProducer → _age → _baseInfo,再执行基类构造函数体。
  2. 再构造派生成员(按 Cow 中的声明顺序,与初始化列表书写顺序无关):_weight → _cowInfo → _ID → _ageAlias;
    其中 _ID 必须在初始化列表给值,_ageAlias 在初始化列表绑定到本对象的 _age。
  3. 最后进入 Cow 构造函数体,打印“Cow 构造”。

拷贝构造调用顺序

  1. 先调用 Livestock(const Livestock&) 拷贝构造基类部分。
  2. 再按 Cow 的成员声明顺序拷贝构造成员:_weight, _cowInfo, _ID, _ageAlias;
    _ID 在初始化列表拷贝,_ageAlias 绑定本对象的 _age(而非对方的引用)。
  3. 执行 Cow 拷贝构造函数体,打印“Cow拷贝构造”。

拷贝赋值调用顺序

  1. 先调用 Livestock::operator=(copy) 给基类部分赋值。
  2. 再给派生的可赋值成员逐个赋值:_weight, _cowInfo;
    _ID(const)不可赋值,_ageAlias(引用)不可改绑。

析构调用顺序

  1. 先执行 Cow 析构函数体并销毁其成员(逆声明顺序)。
  2. 再执行 Livestock 析构函数体并销毁其成员(逆声明顺序)。

函数可见性
using Livestock::introduce; 解除名字隐藏,因此既可调用 Cow::introduce(),也可调用基类重载 introduce(bool)。

在这里插入图片描述

3. 私有继承和保护继承

私有继承

私有继承不同之处在于,指定派生类的基类时使用关键字private

class Base{
    // ...
};
class Derived: private Base{
    // ...
};

私有继承意味着在派生类的实例中,基类的公有成员和方法都是私有的——不能从外部访问。即是==即便是Base类的公有成员和方法,也只能被Derived类使用,而无法通过Derived实例来使用

保护继承

class Base{
    // ...
};
class Derived: protected Base{
    // ...
};

保护继承意味着:在 Derived 的实例里,Base 的公有成员和受保护成员在 Derived 中都变成了 protected。
所以:

  • 外部代码:不能通过 Derived 实例来访问这些成员(对外不可见)。
  • **Derived 自己以及Derived 的子类:可以使用这些从 Base 继承来的成员(在类内/子类内可见)。
  • 对外也不能把 Derived 隐式当作 Base 用(不能隐式转换成 Base* / Base&)。

换句话说——

即便是 Base 的公有成员和方法,在“保护继承”下,也只能被 Derived 以及它的派生类使用,无法通过 Derived 实例在类外直接使用。

以下是各种继承的关系:

类成员/继承方式public继承protected继承private继承
public成员派生类的public成员派生类的protected成员派生类的private成员
protected成员派生类的protected成员派生类的protected成员派生类的private成员
private成员在派生类中不可见在派生类中不可见在派生类中不可见
  1. 基类的public成员

    • 若派生类用public继承:在派生类中仍为public成员(派生类对象可直接访问,派生类的派生类也能按规则继承)。
    • 若派生类用protected继承:在派生类中变为protected成员(派生类对象不可直接访问,但派生类的成员函数和其派生类可访问)。
    • 若派生类用private继承:在派生类中变为private成员(仅派生类自己的成员函数可访问,其派生类无法访问)。
  2. 基类的protected成员

    • 若派生类用public继承:在派生类中仍为protected成员(规则同上,保持 “保护” 特性)。
    • 若派生类用protected继承:在派生类中仍为protected成员(继承后权限不变)。
    • 若派生类用private继承:在派生类中变为private成员(权限被 “收紧” 为私有)。
  3. 基类的private成员

    • 无论派生类用哪种方式继承(public/protected/private),基类的private成员在派生类中完全不可见(派生类的成员函数和对象都无法直接访问,只能通过基类提供的public/protected成员函数间接访问)。

    三种继承方式衍生出九种情况,这样的设计有点冗余,在实践中基本都是使用公有继承

    4. 基类和派生类对象赋值转换

    派生类对象可以赋值给基类的对象、基类的指针、基类的引用,这种行为称为切割,把派生类中基类的那一部分切割后赋值过去。

    但是,基类对象不能赋值给派生类对象

在这里插入图片描述

// 派生类对象可以赋值给基类的对象、指针、引用
class Person {
protected:
	int _age;
	string _sex;
	string _name;
};

class Student : public Person {
protected:
	int _score;
};
int main() {
	Student stuObj;
	Person pA = stuObj;
	Person& pB = stuObj;
	Person* pC = &stuObj;

	// 基类对象不能赋值给派生类对象 小->大 不行
	//stuObj = pA;

	return 0;
}

5. 多继承

多继承是指一个派生类会继承多个基类的属性

  • 好处:能把互不相干的能力(例如“可充电”“可联网”)按横向能力接口拆开,某个具体对象可以一次性“拿来就用”。

  • 风险:若多个基类间存在继承关系,可能出现菱形继承带来的二义性

就比如说生活中的共享电动车,它可充电、可联网、属于电动车

// 多继承
// 共享电动车: 可充电 可联网 是电动车

class eBike {
public:
	void name(){ cout << "是电动车" << endl; }
};
class chargeAble {
public:
	void canCharge() { cout << "可充电" << endl; }
};
class webConnectable {
public:
	void canConnectWeb() { cout << "可联网" << endl; }
};

class shareEBike : public eBike
				 , public chargeAble
				 , public webConnectable{
public:
	void bike() { cout << "是共享电动车" << endl; }
};

int main() {
	shareEBike bike;
	bike.bike();
	bike.name();
	bike.canConnectWeb();
	bike.canCharge();

	return 0;
}

6. final禁止基类被继承

从C++11起,编译器支持限定符final。被声明为final的类不能用作基类。

例如:

class shareEBike final: public eBike
				 , public chargeAble
				 , public webConnectable{
public:
	void bike() { cout << "是共享电动车" << endl; }
};

7. 全文总结

  1. 继承基础(默认用公有继承)
  • 动机/场景:多个组件“有共同属性但细节不同”(外卖系统:骑手/客户/商家)。
    两种做法:
    ① 各写一套(重复);② 抽公共到基类,差异在派生类中扩展/覆盖(继承)。

  • 定义:继承让代码复用并形成层次结构,在保留原有类特性的基础上扩展新功能。

  • 语法/示例(Fish):
    class Derived : public Base {};protected成员仅类族可见。

    class Fish { protected: bool isFreshWaterFish; public: void swim(){...} };
    class Carp : public Fish { public: Carp(){ isFreshWaterFish = true; } };
    class Tuna : public Fish { public: Tuna(){ isFreshWaterFish = false; } };
    
  • 基类初始化(向基类传参):派生类初始化列表调用合适的基构造,避免未初始化。

    class Fish{ protected: bool isFreshWaterFish; 
      public: Fish(bool b):isFreshWaterFish(b){} };
    class Carp: public Fish { public: Carp():Fish(true){} };
    class Tuna: public Fish { public: Tuna():Fish(false){} };
    

在派生类中覆盖/调用/隐藏基类方法

  • 覆盖:派生类用相同函数签名重写基类函数。

    class Base{ public: void func(){} };
    class Derived: public Base{ public: void func(){} }; // 覆盖
    
  • 显式调用基类版本:对象.Base::func()。

  • 名字隐藏:派生类出现同名成员会遮挡基类同名成员(与参数是否相同无关)。
    解除隐藏:在派生类中 using Base::introduce;

  • 示例(Livestock/Cow/Sheep):

    • Sheep::introduce() 覆盖 Livestock::introduce();
    • 通过 Livestock::introduce() 或 using 访问被遮挡的重载。
  1. 派生类中默认成员函数的行为(顺序与规则)
  • 构造顺序:先基类 → 后派生成员(按声明顺序)→ 执行派生构造体。
    常量/引用成员必须在初始化列表赋值/绑定。
  • 拷贝构造:先拷贝基类部分,再拷贝派生成员。
  • 赋值运算:operator= 先赋基类,再赋派生成员;const/引用成员不可重新赋值/改绑。
  • 析构顺序:先析构派生,再析构基类(与构造相反)。
  • 可见性:using Base::introduce; 可解除隐藏,保留基类重载的可见性。
  1. 继承方式:public / protected / private
  • public 继承(推荐):基类 public → 派生 public;语义清晰(is-a)。
  • protected 继承:基类 public/protected 在派生类中都变为 protected(仅类族可用,对外不可见)。
  • private 继承:基类 public/protected 在派生类中都变为 private(对外完全隐藏,更像实现复用)。
  • 共同点:基类的 private 成员对子类不可见(只能经由基类接口访问)。

实务上优先 public 继承,其余两种少用、语义不直观。

  1. 基类与派生类的赋值/转换
  • 派生 → 基类:允许(对象切片 / 指针或引用向上转型)。

  • 基类 → 派生:禁止(不安全)。

    Student stu; Person pa = stu; Person& pr = stu; Person* pp = &stu; // 反向不行
    
  1. 多继承
  • 定义:一个派生类可同时继承多个基类。

  • 优点:可把**互不相关的“能力”*独立为接口,进行*横向组合(比如“可充电”“可联网”)。

  • 风险:若多基类之间存在继承,易引出菱形与二义性

  • 生活化示例:

    class eBike{ public: void name(){ cout<<"是电动车\n"; } };
    class chargeAble{ public: void canCharge(){ cout<<"可充电\n"; } };
    class webConnectable{ public: void canConnectWeb(){ cout<<"可联网\n"; } };
    class shareEBike : public eBike, public chargeAble, public webConnectable {
      public: void bike(){ cout<<"是共享电动车\n"; }
    };
    

    → 接口式多继承

  1. final 禁止被继承(C++11)
  • 作用于类:class X final { ... }; —— X 不可再作基类。
  • 你的示例:class shareEBike final : public eBike, ... { ... };
  1. is-a 与 has-a
  • is-a(是一个):语义上“子类就是基类的一种”。
    → 采用公有继承(如 Tuna : public Fish、shareEBike : public eBike)。
  • has-a(有一个/由……组成):整体拥有部件。
    → 采用组合/聚合(成员对象),不要用继承(如电动车有电池:class EBike { Battery battery_; })。
  • 横向“能力”拼装(既非父子、也非组成):
    → 用接口式多继承(如 Rechargeable + NetworkConnectable)
  • 概念从属 → public 继承(is-a);
  • 组成拥有 → 组合(has-a);
  • 能力叠加 → 接口式多继承(无菱形)。
出现 Tomcat:HTTP状态 404 - 未找到解决方法(图文分析) 出现如下问题 HTTP状态 404 - 未找到 类型 状态报告 消息 请求的资源[/ajax_war_exploded/]不可用 描述 源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示。 Apache Tomcat/8.5.69 解决方法如下: 网上中的操作有步操作多数都误导 这个部署的环境目录上面是web.xml 下面的目录路径是web即可不用到webapp 保存重启服务器即可使用 其他配置中启动tomcat的操作具体 JRE 1.8 ... 阅读详情

相关推荐

Python爬虫实战:用Selenium+ChromeDriver批量抓取政府网站政策文件(附完整代码)

本文详细介绍了使用Python的Selenium和ChromeDriver进行政府网站政策文件批量抓取的实战技巧。从环境配置、动态页面元素定位到分页机制破解和反爬虫策略,提供了完整的解决方案和代码示例,帮助开发者高效获取结构化政策数据。

weixin_42531710的博客 266

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 250

操作系统头歌 课后作业3.3.2:进程的调度

请问:从 6 号进程刚创建完 7 号进程开始,在接下来的 9 次进程调度中,每次调度时,6 号和 7 号进程的剩余时间片是多少?请问:从 6 号进程刚创建完 7 号进程开始,在接下来的 9 次进程调度中,每次调度时,6 号和 7 号进程的剩余时间片是多少?根据相关知识回答当程序 01.c 运行时(在版本 0 内核中),会在父子两个进程(假设分别为 6 号和 7 号进程)中分别输出字符串。当程序 01.c 运行时(在版本 0 内核中),会在父子两个进程(假设分别为 6 号和 7 号进程)中分别输出字符串。

weixin_73255571的博客 4730

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 393

LabVIEW多界面应用开发:从启动器到主界面的切换架构与实现

在图形化编程领域,模块化与界面管理是构建复杂应用程序的核心。其原理在于通过清晰的程序架构,实现不同功能模块间的独立运行与协同工作,从而提升代码的可维护性和可扩展性。这种架构的技术价值在于能够有效管理应用程序的生命周期与资源,避免内存泄漏,并确保流畅的用户体验。在测控系统、自动化测试等工程实践中,常需实现如启动界面选择语言或模式后,无缝切换至主功能界面的场景。本文聚焦于LabVIEW环境,深入探讨如何利用动态调用、状态机等机制,并结合功能全局变量(FGV)与队列消息处理器(QMH)等热词技术,构建健壮的多界面

weixin_30802819的博客 263

智能车实战——从ADC采集到PWM控制,让电磁车精准循迹

本文详细介绍了电磁循迹智能车的实现原理与实战技巧,涵盖从ADC信号采集、PWM控制到PID算法整定的全流程。通过STM32代码示例解析电感信号处理、差比和偏差计算及电机驱动等关键技术,帮助开发者快速掌握智能车精准循迹的核心方法,提升电磁车竞赛项目的开发效率。

weixin_29192211的博客 301

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 345

Valgrind 从入门到精通:内存调试和性能分析

Valgrind 提供了强大的 API,允许开发者编写自定义的检查工具。通过扩展 Valgrind,可以针对特定场景实现更精准的检测。

大模型大数据攻城狮的专栏 1217

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 86

GameFramework框架详解之 Network网络框架

NetworkManager只负责维护NetworkChannel,实际的有种我们简历网络连接的也是NetworkChannel作为一个网路频道,这样管理起来比较方便,比如:一个游戏可能有多个网络连接,那么我们建立多个网络频道即可,或者当我们一个网络频道断线后,我们可以直接再新建一个,防止数据混乱。/// /// 网络管理器。/// /// 网络频道。/// 用户自定义网络错误事件。/// 检查是否存在网络频道。/// 获取网络频道。/// 获取所有网络频道。

TxNet 3367

多层环境室内定位系统综述总结

本文是一篇发表于2025年《IEEE Access》期刊的综述论文,由埃及开罗美国大学与卡塔尔卡内基梅隆大学团队合作完成,聚焦多层环境室内定位系统的核心挑战、技术方案及未来方向,填补了该领域缺乏全面综述的空白。

程序员石磊 445

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 306

GEE训练教程:如何批量从Google assets中将时序影像批量导出到Google drive

本文介绍了一种使用Google Earth Engine(GEE)批量导出2000-2023年叶绿素a(Chl-a)浓度数据的方法。通过JavaScript代码自动生成多个年份的导出任务,将数据保存至Google Drive指定文件夹。文章详细说明了代码参数配置、使用步骤和注意事项,包括资产命名规则验证、导出任务启动方式,以及分辨率、区域范围等关键参数的调整建议。同时提供了常见问题的解决方案,如处理缺失年份数据、解决导出像素超限等问题。该方法可显著提高多年份Chl-a数据批量导出的效率,适用于水体环境研究的

此星光明博客 91

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 447

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 234

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级 备考:莫队算法离线区间查询详解

备赛进行时:第十八届蓝桥杯软件赛报名已于 2026-10-08 10:00 开启、12-18 14:00 截止,省赛定在 2027-04-10(C/C++·Python 程序设计 9:00–13:00);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)考级报名 9/23–12/2 16:00、统考 12-12;全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛也已进入复盘练兵窗口。

IT_Scratch的博客 163

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

【C++ 基础入门】C++继承(下):问题、底层原理与组合

C++继承(菱形继承的问题、虚继承原理、继承与组合、构造顺序)

2601_96587588的博客 797

825 mW 顶上 212 °C:ASM491S 的 105 °C/W、250 mA 短路电流与热关断

25 °C 环境里结温 34.1 °C,125 °C 环境里结温 134.1 °C——后者已经离 150 °C 只有 16 °C 了,但还在范围内。换个角度看同一件事:从 25 °C 和从 125 °C 起算,温升都是 86.6 °C,但一个到 111.6 °C(还在范围内),一个到 211.6 °C(远远超限)。这个算例里 20 °C/W 是我假设的,实际值取决于机壳材料、密封程度、有没有风扇,跨度可以从 5 °C/W 到 50 °C/W 以上。外面 50 °C 的机箱,盒内就是 63.9 °C。

ANSILIC的博客 373

《C++ 项目:手写大模型接入 SDK》Gemini接口设计(上):初始化、全量响应实现和测试

本文系统完成了Gemini模型通过OpenAI兼容API的接入开发,基于Apifox验证接口规范,设计并实现GeminiProvider类,涵盖初始化、状态检测、全量响应等核心功能。重点处理了代理配置强制校验、Bearer鉴权、JSON序列化与响应解析等工程细节,并通过GTest与CMake完成单元测试,验证链路可用性。项目遵循策略模式,保持与DeepSeek、ChatGPT等模型的统一抽象接口,显著提升SDK可扩展性。

2501_91275995的博客 395

【容斥原理计数】题解:P9489 ZHY的表示法_容斥原理_计数问题_前缀和问题转化_算法竞赛C++

给定 $ n $ 个正整数,求区间 $[l,r]$ 中有多少正整数 $ x $ 可表示为 $ \sum \lfloor \frac{y}{x_i} \rfloor $。通过二分确定最大 $ y $,转化为求 $ 1 \sim H $ 中是某些 $ x_i $ 倍数的数的个数,利用容斥原理与 DFS 枚举子集计算,结合 LCM 预判剪枝及去重优化,时间复杂度 $ O(2^n \log V) $,可高效处理 $ r \le 10^9 $ 的数据。

YLCHUP的博客 104

头歌 数据结构——C&C++线性表实训

本系列任务围绕线性表的链式实现,依次完成顺序构建、逆序构建、排序插入、查找、删除、求长度及栈、队列、集合等应用。通过动态内存分配与指针操作,掌握单链表的基本操作与实际应用,涵盖插入、删除、遍历、搜索等核心功能,最终实现数据结构在典型场景中的灵活运用。

一名敲代码的苦13程序员 241

字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)

本题核心思想是:最少移动步数 = 总长度 - 可保留的最长连续子串长度。未移动字母在T中必须为连续子串,在S中需保持相对顺序(即子序列)。通过枚举T的所有连续子串起点,贪心匹配S中的对应字符,求出最长可保留长度L,答案即为n−L。时间复杂度O(n²),空间复杂度O(n),适用于n≤1000。

冻柠茶飞冰走茶 588

C++ vector 添加与删除元素的底层原理:erase 返回值与倒序遍历删除

vector是连续内存的动态数组,支持O(1)随机访问,但扩容时需重新分配内存并搬迁元素。push_back和emplace_back均摊时间复杂度为O(1),扩容策略(如2倍或1.5倍)确保总代价均摊;insert与erase在中间位置操作需移动元素,时间复杂度O(n),且使后续迭代器失效。删除指定元素推荐使用erase-remove惯用法或std::erase_if,避免逐个删除导致O(n²)复杂度。从后往前遍历可防止漏删,但最优解仍是高效惯用法。预估容量后调用reserve()可避免频繁扩容,提升性能

2402_85743155的博客 798

C++ 标准库算法全景指南

本文系统梳理C++标准库<algorithm>与<numeric>中100+算法,按功能分为非修改、修改、排序、二分查找、归并、堆、极值、字典序、排列及数值操作十大类,涵盖典型用法与时间复杂度。结合C++20范围(ranges)与投影特性,实现更简洁高效的代码。

sss123ddd的博客 375

CuTe 与 CuTe DSL:从 C++ 模板元编程到 Python DSL 的 GPU 编程演进

CuTe 的价值不在于它"好用",而在于它把 GPU 编程中最难表达的部分——布局与线程映射——变成了可以做代数运算的数学对象。它坚持 C++ 模板路线,拒绝引入编译器黑盒,换来了极致的可控性与性能上限,代价是陡峭的学习曲线与糟糕的报错体验。CuTe DSL 的出现,说明这个团队也意识到了"曲高和寡"的问题,正在尝试用 Python 的易用性包裹 C++ 模板的强大。未来 CuTe 大概率会继续扮演"底层布局中间层"的角色,而 DSL 层则负责把这份能力交付给更多开发者。

Maxwell_Newton的博客 256

C++ string 的接口使用、字符串处理与深浅拷贝实现

本文系统讲解C++ string类的接口使用、字符串处理技巧及深浅拷贝实现。重点对比C风格字符串的缺陷,阐明string在容量管理(size/capacity)、遍历(auto/range for)、修改操作上的优势;通过双指针扫描、计数统计等案例解析字符串处理逻辑;深入剖析浅拷贝风险与深拷贝实现,涵盖传统版与现代版资源管理策略,并简要介绍写时拷贝机制,帮助读者掌握高效安全的字符串编程实践。

2601_96348813的博客 245
上一篇: 字符串相关OJ题解析(图文并茂+过程演示)
下一篇: “码”上认亲!面向对象之多态:一脉相承的「同名不同用」
Vect__
博客等级 码龄1年 204粉丝 74原创
评论 50
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值