C++ 多态 - 教程

news/2025/12/4 12:54:46/文章来源:https://www.cnblogs.com/tlnshuju/p/19306914

C++ 多态 - 教程

2025-12-04 12:52  tlnshuju  阅读(0)  评论(0)    收藏  举报

一、多态的概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

举个栗子:比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。

二、多态的定义及实现

多态的构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。


那么在继承中要构成多态还有两个条件

  1. 必须通过基类的指针或者引用调用虚函数。
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

虚函数

virtual关键字修饰的类成员函数,被称为虚函数。

class Person
{
public://被virtual修饰的类成员函数virtual void BuyTicket(){cout << "买票-全价" << endl;}
};

需要注意的是:

  1. 只有类的非静态成员函数前可以加virtual,普通函数前不能加virtual。
  2. 虚函数这里的virtual和虚继承中的virtual是同一个关键字,但是它们之间没有任何关系。虚函数这里的virtual是为了实现多态,而虚继承的virtual是为了解决菱形继承的数据冗余和二义性。

虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数( 即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同 ),称子类的虚函数重写了基类的虚函数。

下面,我们分别实现两个类,一个Person类作为子类,另一个Student类作为子类。                       

// 构成多态
// 条件一:父类的引用或指针去调用虚函数(虚函数:用virtual修饰的函数)
// 条件二:虚函数的重写(父子类中的虚函数三同(函数名、返回值、参数都相同))
class Person {
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}
};
class Student : public Person {
public:virtual void BuyTicket(){cout << "买票-半价" << endl;}
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person p;Student s;//p.BuyTicket();//s.BuyTicket();Func(p);Func(s);return 0;
}

我们就可以通过父类Person的指针或者引用调用虚函数BuyTicket,此时不同类型的对象,调用的就是不同的函数,产生的也是不同的结果,进而实现了函数调用的多种形态。

多态调用 -> 看指针或引用指向的对象。

普通调用 -> 看指针或引用或调用对象 的类型。

这里Func函数的形参P就是Person类引用的别名,形参P可以引用基类对象,也可以引用派生类对
象。
引用派生类对象就调用派生类中的虚函数,引用了基类对象就调用基类中的虚函数。

注意:

 在重写基类虚函数时,派生类的虚函数不加virtual关键字也可以构成重写,主要原因是因为继承后基类的虚函数被继承下来了,在派生类中依旧保持虚函数属性。但是这种写法不是很规范,因此建议在派生类的虚函数前也加上virtual关键字

虚函数重写的两个特殊情况

协变(基类与派生类虚函数的返回值类型不同)

派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

class A {};
class B : public A{};
// 协变 -- 虚函数的返回值不同,但是返回的类型必须是父子类关系的指针或引用
class Person {
public:virtual A* f(){cout << "A::f()" << endl;return new A;}
};
class Student : public Person {
public:virtual B* f(){cout << "B::f()" << endl;return new B;}
};
int main()
{Person *p1 = new Person;Person *p2 = new Student;p2 -> f();return 0;
}

两种错误的协变写法:
1、这种情况就是返回值类型并不是父子类关系,A既不是B的基类也不是B的派生类。所以不构成协变。
这里的两个虚函数不构成协变的关系,那就构成了隐藏的关系(不是重载 -- 构成重载的条件是两个函数得在同一作用域中)。

class A {};
class B {};
// 协变 -- 虚函数的返回值不同,但是返回的类型必须是父子类关系的指针或引用
class Person {
public:virtual A* f(){cout << "A::f()" << endl;return new A;}
};
class Student : public Person {
public:virtual B* f(){cout << "B::f()" << endl;return new B;}
};

2、这种情况就是虽然返回值类型是父子关系,但是返回的不是指针或引用,返回的是对象。

class A {};
class B : public A{};
// 协变 -- 虚函数的返回值不同,但是返回的类型必须是父子类关系的指针或引用
class Person {
public:virtual A f(){cout << "A::f()" << endl;return new A;}
};
class Student : public Person {
public:virtual B f(){cout << "B::f()" << endl;return new B;}
};

析构函数的重写(基类与派生类析构函数的名字不同)

基类的析构函数为虚函数此时派生类析构函数只要定义,无论是否加virtual关键字某类的析构函数构成重写,虽然基类与派生类析构函数名字不同。

虽然函数名不相同看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理编译后析构函数的名称统一处理成destructor。

class Person {
public:~Person(){cout << "~Person()" << endl;}
};
// 重写 -> 重写实现(继承父类的接口并重写继承下来的接口的实现)
class Student : public Person {
public:~Student(){cout << "~Student()" << endl;}
};
int main()
{//Person p;//Student s;Person* p1 = new Person();delete p1;Person* p2 = new Student();delete p2;return 0;
}

按照继承的关系,构造时先构造父类再构造子类,然而再构造子类的时候得先把从父类继承下来的成员初始化。这样还得调用父类的构造函数,随后才能调用子类的构造函数。
随后析构时,先调用子类的析构函数,然后子类中继承父类成员调用父类的析构函数,最后才调用父类的析构函数。

如果析构函数不构成重写,那对象调用析构函数就是普通调用。
普通调用就是依据调用对象来调用对应的析构函数。
因为p1,p2的类型是Person类,所以就只能调用Person类的析构函数。


如果是多态调用,就会按照指针指向或引用的对象来调用对应的析构函数。                                 p1是指向Person,所以p1调用Person的析构函数。                                                                      p2是指向Student,所以p2调用Student的析构函数

delete底层是会调用析构函数
析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor0),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。


基类和派生类的析构函数名相同,返回值类型相同,参数列表相同。只要用virtual修饰,就构成了虚函数重写。

class Person {
public:virtual ~Person(){cout << "~Person()" << endl;}
};
// 重写 -> 重写实现(继承父类的接口并重写继承下来的接口的实现)
class Student : public Person {
public:virtual ~Student(){cout << "~Student()" << endl;}
};

虚函数重写时,父类的函数用了virtual修饰,子类的函数不加也构成重写。建议:都给虚函数加上virtual进行修饰。

class Person {
public:~Person(){cout << "~Person()" << endl;}virtual void BuyTicket(){cout << "买票 - 全价" << endl;}
};
// 重写 -> 重写实现(继承父类的接口并重写继承下来的接口的实现)
class Student : public Person {
public:~Student(){cout << "~Student()" << endl;}void BuyTicket(){cout << "买票 - 半价" << endl;}
};
int main()
{//Person p;//Student s;Person* p1 = new Person();p1->BuyTicket();//delete p1;Person* p2 = new Student();p2->BuyTicket();//delete p2;return 0;
}

重载、重写(覆盖)、重定义(隐藏)的对比

C++11override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了overridefinal两个关键字,可以帮助用户检测是否重写。

final

修饰虚函数,表示该虚函数不会再被重写。

// final:修饰虚函数,表示该虚函数不能再被重写
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() { cout << "Benz-舒适" << endl; }
};

override

检查派生类虚函数是否重写基类中某个虚函数,如果没有则报错。

// override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
class Car {
public:void Drive() {}
};
class Benz :public Car {
public:virtual void Drive() override { cout << "Benz-舒适" << endl; }
};

三、抽象类

概念

在虚函数的后面写上 =0,则这函数为纯虚函数
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象

// 抽象类
class Car
{
public:// 纯虚函数virtual void Drive() = 0;
};

抽象类不能实例化出对象。派生类继承后也不能实例化出对象。只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

// 抽象类
class Car
{
public:// 纯虚函数virtual void Drive() = 0;
};
// 强制子类重写虚函数
class Benz :public Car
{
public:void Drive(){cout << "Benz-舒适" << endl;}
};

抽象类使用的场景:

不想父类实例化出对象,并且想要父类中的某个虚函数必须在子类中进行重写的就可以使用抽象类。

接口继承和实现继承

实现继承:普通函数的继承是一种实现继承,派生类继承了基类函数的实现,可以使用该函数。

接口继承:虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态。

建议:如果不实现多态就不要把函数设为虚函数。

四、多态的原理

虚函数表

给出一段代码,看下Base类实例化对象的大小是多少?

class Base
{
public:Base():_b(2),ch('a'){}void Func1(){}
private:int _b = 1;char ch = 'c';
};

通过结果可以看出,Base类实例化对象的大小为12字节。

首先我们可以知道,类的成员函数并不会存放在类中,而是存放在一个公共的区域。

因此这个大小为12字节的对象是怎么生成的?

从对象模型可以看出,有一个指针_vpftr放在对象成员变量之前(有些平台可能会将_vfptr放在对象成员变量之后,这个视平台而定),这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。

虚函数表中到底放的是什么?

下列代码中。父类Base中有三个成员函数,其中Func1和Func2是虚函数,Func3是普通成员函数,子类Derive当中仅对父类的Func1函数进行了重写。

class Base
{
public:Base():_b(2),ch('a'){}virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;char ch = 'c';
};
class D : public Base
{
public:void Func1(){cout << "D::Func1()" << endl;}
private:int _d = 2;
};

我们先看下基类对象和派生类对象的模型

通过派生类的对象模型可以看出,派生类对象由两个部分组成,一个是从基类继承下来的成员,另一个则是派生类自己的成员和虚函数表指针。

通过对象模型也可以看出基类对象和派生类对象的虚函数表是不一样的,派生类虽然继承了积累的三个成员函数,但只对func1进行了重写。所以d的虚表中存的是重写的Func1和继承基类的Func2。但由于继承下来的Func3并不是虚函数,所以就没放到派生类的虚函数表中。

所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。

此外,虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr。

总结一下派生类的虚表生成

  1. 先将基类中的虚表内容拷贝一份到派生类虚表中
  2. 如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
  3. 派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

还有个问题,虚函数存在哪的?虚表存在哪的?

虚表实际上是在构造函数初始化列表阶段进行初始化的,这从下图就可以看出。其次注意虚表当中存的是虚函数的地址不是虚函数,虚函数和普通函数一样,都是存在代码段的,只是他的地址又存到了虚表当中。另外,对象中存的不是虚表而是指向虚表的指针。

至于虚表是存在哪里的,我们可以通过以下这段代码进行判断。

int j = 0;
int main()
{Base b;Base* p = &b;printf("vfptr:%p\n", *((int*)p)); //000FDCACint i = 0;printf("栈上地址:%p\n", &i);       //005CFE24printf("数据段地址:%p\n", &j);     //0010038Cint* k = new int;printf("堆上地址:%p\n", k);       //00A6CA00char* cp = "hello world";printf("代码段地址:%p\n", cp);    //000FDCB4return 0;
}

代码当中打印了对象b当中的虚表指针,也就是虚表的地址,可以发现虚表地址与代码段的地址非常接近,由此我们可以得出虚表实际上是存在代码段的。

多态的原理

例如,下面代码中,为什么当父类Person指针指向的是父类对象Mike时,调用的就是父类的BuyTicket,当父类Person指针指向的是子类对象Johnson时,调用的就是子类的BuyTicket?

#include 
using namespace std;
//父类
class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}int _p = 1;
};
//子类
class Student : public Person
{
public:virtual void BuyTicket(){cout << "买票-半价" << endl;}int _s = 2;
};
int main()
{Person Mike;Student Johnson;Johnson._p = 3; //以便观察是否完成切片Person* p1 = &Mike;Person* p2 = &Johnson;p1->BuyTicket(); //买票-全价p2->BuyTicket(); //买票-半价return 0;
}

通过调试可以发现,对象Mike中包含一个成员变量_p和一个虚表指针,对象Johnson中包含两个成员变量_p和_s以及一个虚表指针,这两个对象当中的虚表指针分别指向自己的虚表。

绕此图分析便可得到多态的原理:

  • 父类指针p1指向Mike对象,p1->BuyTicket在Mike的虚表中找到的虚函数就是Person::BuyTicket。
  • 父类指针p2指向Johnson对象,p2>BuyTicket在Johnson的虚表中找到的虚函数就是Student::BuyTicket。

这样就实现出了不同对象去完成同一行为时,展现出不同的形态。

使用父类指针或者引用时,实际上是一种切片行为,切片时只会让父类指针或者引用得到父类对象或子类对象中切出来的那一部分。

因此,我们后序用p1和p2调用虚函数时,p1和p2通过虚表指针找到的虚表是不一样的,最终调用的函数也是不一样的。

使用父类对象时,切片得到部分成员变量后,会调用父类的拷贝构造函数对那部分成员变量进行拷贝构造,而拷贝构造出来的父类对象p1和p2当中的虚表指针指向的都是父类对象的虚表。因为同类型的对象共享一张虚表,他们的虚表指针指向的虚表是一样的。

因此,我们后序用p1和p2调用虚函数时,p1和p2通过虚表指针找到的虚表是一样的,最终调用的函数也是一样的,也就无法构成多态。

总结一下:

  1. 构成多态,指向谁就调用谁的虚函数,跟对象有关。
  2. 不构成多态,对象类型是什么就调用谁的虚函数,跟类型有关。

静态绑定和动态绑定

静态绑定:静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载。


动态绑定:动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态 (运行时,根据指向的对象去到对应对象的类的虚函数表中寻找函数地址)。

通过下列代码的汇编代码进一步理解静态绑定和动态绑定。

//父类
class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}
};
//子类
class Student : public Person
{
public:virtual void BuyTicket(){cout << "买票-半价" << endl;}
};

如果不按照多态的方式调用函数,那函数的调用是在编译时就确定了。

将调用的那条语句转成汇编语句就只有两条汇编指令

如果按照多态的方式调用函数,那函数的调用是在运行时才确定。

相比不构成多态时的代码,构成多态时调用函数的那句代码翻译成汇编后就变成了八条汇编指令,主要原因就是我们需要在运行时,先到指定对象的虚表中找到要调用的虚函数,然后才能进行函数的调用。

五、单继承和多继承关系的虚函数表

单继承下的虚函数表

通过下列代码来看下单继承关系下基类和派生类的模型。

class Base {
public :
virtual void func1()
{cout<<"Base::func1" <

基类和派生类的模型如下图所示:

在单继承关系当中,派生类的虚表生成过程如下:

  1. 继承基类的虚表内容到派生类的虚表。
  2. 对派生类重写了的虚函数地址进行覆盖,比如func1。
  3. 虚表当中新增派生类当中新的虚函数地址,比如func3和func4。

观察下图中的监视窗口中我们发现看不见func3和func4。这里是编译器的监视窗口故意隐藏了这两个函数,也可以认为是他的一个小bug。那么我们如何查看d的虚表呢?下面我们使用代码打印出虚表中的函数。

// 给函数指针定义别名
typedef void (*VF_ptr)();
// 打印虚表每个函数的地址
// 在vs下,虚表是由空指针结尾。所以遍历虚表(函数指针数组)的条件就以数组元素不为空
void PrintVTB(VF_ptr* vtf)
{for (int i = 0; vtf[i] != nullptr; i++){printf("[%d]: %p -> ", i ,vtf[i]);VF_ptr f = vtf[i];// 两种函数指针调用的方法f();//(*f)();//vtf[i]();//(*vtf[i])();}
}
int main()
{Base b;PrintVTB((VF_ptr*)(*(int*)&b));D d;PrintVTB((VF_ptr*)(*(int*)&d));return 0;
}

运行结果如下:

这段代码的思路如下:

思路:取出b、d对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面放了一个nullptr(虚函数表由nullptr结尾)

  1. 先取b的地址,强转成一个int*的指针
  2. 再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针
  3. 再强转成VFPTR*,因为虚表就是一个存VFPTR类型(虚函数指针类型)的数组。
  4. 虚表指针传递给PrintVTable进行打印虚表
  5. 需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最后面没有放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的-生成-清理解决方案,再编译就好了。

多继承下的虚函数表

通过下列代码来看下单继承关系下基类和派生类的模型。

class Base1 {
public:virtual void func1() { cout << "Base1::func1" << endl; }virtual void func2() { cout << "Base1::func2" << endl; }
private:int b1 = 1;
};
class Base2 {
public:virtual void func1() { cout << "Base2::func1" << endl; }virtual void func2() { cout << "Base2::func2" << endl; }
private:int b2 = 2;
};
class Derive : public Base1, public Base2 {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }
private:int d1 = 3;
};
int main()
{Derive d;//cout << "d_size:" << sizeof(d) << endl;Base1* b1 = &d;Base2* b2 = &d;b1->func1();b2->func1();PrintVTB((VF_ptr*)(*(int*)b1));cout << endl;PrintVTB((VF_ptr*)(*(int*)b2));cout << endl;//PrintVTB((VF_ptr*)(*(int*)&d));return 0;
}

派生类对象的模型如下图所示:

在多继承关系当中,派生类的虚表生成过程如下:

  1. 分别继承各个基类的虚表内容到派生类的各个虚表当中。
  2. 对派生类重写了的虚函数地址进行覆盖(派生类中的各个虚表中存有该被重写虚函数地址的都需要进行覆盖),比如func1。
  3. 在派生类第一个继承基类部分的虚表当中新增派生类当中新的虚函数地址,比如func3。

通过运行结果能看出完整的虚表内容:

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

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

相关文章

DP 优化方法大杂烩

标 * 的是推荐阅读的部分 / 做的题目。 非常推荐在 cnblogs 中阅读。 1. 动态 DP(DDP)算法简介 动态动态规划。 以 P4719 为例讲一讲 ddp: 1.1. 树剖解法 如果没有修改操作,那么可以设计出 DP 方案 \(f_{i,0/1}\) …

2025年长三角十大方矩管加工厂推荐,矩形方矩管与20#方矩

在基建浪潮与制造业升级的双重驱动下,方矩管作为钢结构、汽车制造、物流货架等领域的核心型材,其质量稳定性与供应效率直接影响工程进度与产品品质。面对市场上良莠不齐的方矩管供应商,如何挑选兼具技术实力、定制能…

拒绝踩坑!羽绒被选购指南 + 年度口碑品牌测评:鸿基羽绒凭什么成为品质之选?

拒绝踩坑!羽绒被选购指南 + 年度口碑品牌测评:鸿基羽绒凭什么成为品质之选?一、冬季选羽绒被,这几个核心指标决定品质好坏 (一)看绒质:选对羽绒类型,保暖与舒适双在线 羽绒被的核心是填充物,主流分为鹅绒和鸭…

2025温州奢侈品名包回收TOP5权威推荐:诚信名包回收门店

二手奢侈品回收市场规模逐年攀升,但消费者常因信息不对称陷入压价套路真伪错判隐私泄露等困境。2024年行业数据显示,超60%的名包回收纠纷源于价格不透明,35%涉及鉴定失误。面对混乱市场,本榜单基于诚信度、鉴定专业…

羽绒被买什么牌子好?从原料到工艺,深度解析 5 大优质品牌的核心优势

羽绒被买什么牌子好?从原料到工艺,深度解析 5 大优质品牌的核心优势一、羽绒被选购难题,如何找到 “最优解”? 冬天来临,一款兼具保暖性、舒适性与安全性的羽绒被成为家庭刚需。面对市场上琳琅满目的品牌,消费者…

2025科研实验室耗材品牌TOP5权威测评:芯硅谷的产品实用

科研实验室耗材是支撑基础研究、药物研发、新能源材料创新的隐形基石,其实用性、耐用性直接影响实验效率与结果可靠性。2024年国内实验室耗材市场规模突破320亿元,年增速18%,但用户对实用性适配场景、耐用性抗造程度…

2025年中国源头温度变送器厂家推荐:温度变送器老牌厂家、温

本榜单基于全国工业传感器市场调研与真实客户口碑,聚焦源头实力性能稳定性行业适配性三大核心维度,筛选出五家标杆温度变送器厂家,为制药、化工、新能源等行业企业选型提供客观参考,助力精准匹配高性价比解决方案。…

想选一床安心羽绒被?看遍行业后,我锁定了这个专注高端制造的 “隐形冠军”

想选一床安心羽绒被?看遍行业后,我锁定了这个专注高端制造的 “隐形冠军”一、为什么选羽绒被?如何锁定真正的 “品质黑马”?​ 冬天的幸福感,一半来自一床好羽绒被。但面对市场上琳琅满目的品牌,“蓬松度虚标”…

无味羽绒被推荐哪家?这几款从源头告别异味,敏感肌放心囤

无味羽绒被推荐哪家?这几款从源头告别异味,敏感肌放心囤无味羽绒被推荐哪家?天气一冷,被窝的温暖度直接决定幸福感,但不少人买羽绒被都踩过“异味坑”——刚拆封一股腥味,晒了好几遍还是散不去,敏感肌躺进去更是…

选广东好羽绒?鸿基羽绒入选2025安心名录,值得信赖

选广东好羽绒?鸿基羽绒入选2025安心名录,值得信赖在“中国羽绒之乡”广东,优质羽绒供应商的选择关乎产品品质与消费安心。面对粉尘过敏、保暖不均等行业痛点,成立于1996年的鸿基羽绒早已树立标杆。以科技赋能羽绒为…

2025年语言培训学校排行:日语语言培训机构前十强与行业全解

在全球化浪潮下,日语、韩语等小语种能力已成为职场竞争力的隐形加分项。无论是留学备考、职场晋升还是兴趣学习,选择靠谱的语言培训机构是效率提升的关键。以下结合教学质量、师资实力、学员口碑等维度,为你盘点202…

2025年十大智能门窗品牌推荐,专业安全系统门窗企业全解析

在消费升级与家居品质化浪潮下,门窗已从遮风挡雨的工具升级为守护家居安全、提升生活质感的核心系统。面对市场上鱼龙混杂的产品,如何挑选兼具智能科技、断桥铝性能与卓越品质的门窗?以下为你推荐2025年在十大智能门…

不二越NACHI高精度轴承供货能力最强的十大代理商

本次榜单联合 NACHI 中国区认证中心与第三方机构中机检测,参照 FAGMA 2025 测评体系,经两个月核查而成,拒绝商业干预,每一项评分都有迹可循一、问题:当精密轴承遇上 “隐形陷阱” 我走访过二十余家制造企业后,那…

2025年12月儿童助听器验配机构推荐榜:五家专业机构对比与选择指南

一、引言 儿童助听器验配是听力康复领域的关键环节,直接关系到听障儿童的言语发育、学习能力及社会融入。本文主要面向听障儿童家长、康复机构从业者及儿科医生等群体,其核心需求包括确保验配精准性、保障长期服务质…

2025年12月儿童助听器验配机构对比评测榜:专业服务与科学验配指南

一、引言 儿童听力健康是影响语言发展、学习能力与社交融入的关键因素,选择专业的助听器验配机构对听障儿童家庭至关重要。本文面向需为儿童配置助听器的家长、康复机构工作者及医疗从业者,深入分析其在控制成本、保…

【Azure Policy】实现拒绝新建/可以修改已存在资源的 Azure Policy 方案

问题描述 在使用Azure的日常工作中,很可能面临这样的情况。为了避免资源的随意创建或是从成本考虑,只能对已有资源进行修改,不允许创建新的资源。在Azure上,是否有方案可以实现这一需求呢? 第一时间想到的方案是使…

2025年12月儿童助听器验配机构推荐榜:专业评估与客观排名对比分析

一、引言 儿童听力健康是影响语言发育和学习能力的关键因素,选择专业的助听器验配机构对听障儿童的康复至关重要。本文面向听障儿童家长、康复机构工作者及医疗采购人员,深入分析其在控制成本、保障验配质量、提升康…

2025年五大信誉佳的齐纳式安全栅品牌公司推荐,高效的齐纳式

在工业自动化领域,齐纳式安全栅作为保障危险工况信号安全传输的核心设备,其性能稳定性与品牌信誉直接关系到生产安全与系统可靠运行。面对市场上鱼龙混杂的供应商,如何选择信誉佳的齐纳式安全栅企业?以下结合产品性…

2025年哈尔滨全屋定制公司五大口碑排名:爱木木业产品怎么样

当199元/㎡全屋定制套餐的广告充斥社交平台,当免费设计背后藏着增项套路,当颗粒板冒充实木芯板的纠纷频发——哈尔滨的业主在全屋定制的选择上,早已从看价格转向比品质、查口碑。2025年,我们结合业主实测、工艺检测…

2025年断桥铝门窗五大品牌推荐,断桥铝门窗知名品牌全解析

在追求品质生活的当下,一扇优质的断桥铝门窗是守护家居舒适与安全的隐形卫士,关乎居住体验与家庭安全。面对市场上鱼龙混杂的断桥铝门窗产品,如何挑选靠谱品牌?以下依据产品性能、服务品质与市场口碑,为你推荐202…