Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >C++程序诗篇的灵动赋形:多态

C++程序诗篇的灵动赋形:多态

作者头像
DARLING Zero two
发布于 2025-04-11 02:27:52
发布于 2025-04-11 02:27:52
7410
代码可运行
举报
文章被收录于专栏:C语言C语言
运行总次数:0
代码可运行

本篇将开启 C++ 三大特性中的多态篇章,多态允许你以统一的方式处理不同类型的对象,通过相同的接口来调用不同的实现方法。这意味着你可以编写通用的代码,而这些代码可以在运行时根据对象的实际类型来执行特定的操作

1.什么是多态?

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

✏️举个例子:

比如买高铁票的时候,我们都属于 Person 类,买的时候会显示为全价,那么我们又属于 Student 类,继承于 Person 类,这时买的时候又会显示为半价,假设两个类都有 BuyTicket 函数,那么相同的函数在继承的基础上,能够实现不同的功能,这就是多态

2.多态的语法实现

2.1 虚函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Person 
{
public:
	virtual void BuyTicket() 
	{ 
		cout << "买票-全价" << endl;
	}
};

virtual 修饰的类成员函数称为虚函数,注意这里和菱形虚拟继承的 virtual 没有关系,不过使用了同一个关键字而已

🔥值得注意的是:

  • 内联函数一般不能是虚函数。内联函数是在编译时将函数体插入到调用处,而虚函数是在运行时进行动态绑定的,两者特性冲突
  • 静态成员不可以是虚函数,虚函数是通过对象的虚函数表指针来实现动态绑定的,也就是在运行时根据对象的实际类型来确定调用哪个虚函数。而静态成员函数是属于类的,不依赖于具体对象,没有对象的概念,也没有虚函数表指针,无法通过动态绑定来调用
  • 构造函数不可以是虚函数,对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的

2.2 多态的构成

虚函数是实现多态的重要组成部分,将上面举的例子以代码形式实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Person 
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-全价" << endl;
	}
};

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

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

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

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

🔥值得注意的是: 多态构成条件缺一不可,如果多态产生问题,子类没有对某个方法进行重写,那么子类对象在调用该方法时,就会沿着继承链向上查找,找到父类中对应的方法并调用

2.3 虚函数的重写

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Person 
{
public:
	virtual void BuyTicket() 
	{ 
		cout << "买票全价" << endl; 
	}
};

class Student : public Person 
{
public:
	virtual void BuyTicket() 
	{ 
		cout << "买票半价" << endl; 
	}
};

void Func(Person& people)
{
	people.BuyTicket();
}

int main()
{
	Person Mike;
	Func(Mike);

	Student Johnson;
	Func(Johnson);
	
	return 0;
}

Person 类的 BuyTicketStudent 类的 BuyTicket 构成重写

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

🔥值得注意的是: 在重写父类虚函数时,子类的虚函数在不加 virtual 关键字时,虽然也可以构成重写(因为继承后父类的虚函数被继承下来了在子类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用

2.3.1 协变
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {};
class B : public A {};

class Person 
{
public:
	virtual A* f() 
	{ 
		return new A; 
	}
};

class Student : public Person 
{
public:
	virtual B* f() 
	{ 
		return new B; 
	}
};

协变是重写的一种特殊情况,简单来说协变就是派生类重写基类虚函数时,与基类虚函数返回值类型不同,且要求父类虚函数类型和子类虚函数类型必须是父子关系的引用和指针

🔥值得注意的是: 必须都是引用或者都是指针,不能一个是引用一个是指针

2.3.2 析构函数的重写
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Person 
{
public:
	virtual ~Person() 
	{ 
		cout << "~Person()" << endl; 
	}
};

class Student : public Person 
{
public:
	virtual ~Student() 
	{ 
		cout << "~Student()" << endl; 
		delete[] ptr;
	}

protected:
	int* ptr = new int[10];
};

int main()
{
	Person* p = new Person;
	delete p;
	p = new Student;
	delete p;
	return 0;
}

这里单纯讲解很难理解,所以以一段代码场景+一些提问来解析:

🚩析构函数+virtual,是不是虚函数重写?

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

🚩为什么要处理成统一名字?

因为要让两个析构函数构成重写

🚩为什么要让他们构成重写?

假设我们上面的这个代码没有加 virtual,运行代码如下:

观察可以发现子类 Student 部分没有得到释放,那么 ptr 指向的空间就会造成内存泄漏

根据 C++ 内存管理学的知识可知

p -> destructor() + operator delete

这里只能调用 p 这个类型的析构函数,但是我们为了实现能够调用指向空间的析构函数,期望是个多态调用,而不是普通调用,所以必须让这两个析构函数构成重写

🔥值得注意的是:

  • 当使用父类指针指向子类对象,析构该指针时,如果父类的析构函数不是虚函数,那么将按指针本身的类型(即父类)来析构。这可能会导致子类部分的资源没有被正确释放,产生内存泄漏等问题
  • 如果父类的析构函数是虚函数,那么会按照指针实际指向的对象类型(即子类)来析构

2.4 override 和 final

🚩final:修饰虚函数,表示该虚函数不能再被重写

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Car
{
public:
	virtual void Drive() final 
	{}
};

class Benz :public Car
{
public:
	virtual void Drive() 
	{ 
		cout << "Benz-舒适" << endl; 
	}
};

🔥值得注意的是:

假设有个 A 类和 B 类,不想让 B 类继承 A 类,那么可以写做:class A final,避免 A 类被继承,这是 C++11 才支持的,在这之前使用的是将 A 的构造函数私有化的方法

🚩override:检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Car 
{
public:
	virtual void Drive() 
	{}
};

class Benz :public Car 
{
public:
	virtual void Drive() override 
	{ 
		cout << "Benz-舒适" << endl; 
	}
};

3.抽象类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Car
{
public:
	virtual void Drive() = 0;
};

class Benz :public Car
{
public:
	virtual void Drive()
	{
		cout << "Benz-舒适" << endl;
	}
};

class BMW :public Car
{
public:
	virtual void Drive()
	{
		cout << "BMW-操控" << endl;
	}
};

void Test()
{
	Car* pBenz = new Benz;
	pBenz->Drive();//访问Benz的虚函数

	Car* pBMW = new BMW;
	pBMW->Drive();//访问BMW的虚函数
}

在虚函数的后面写上 = 0 ,则这个函数为纯虚函数,包含纯虚函数的类叫做抽象类(也叫接口类

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

🔥值得注意的是:

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数

4.多态原理

4.1 虚函数表

✏️以下我们通过多个例子进行详细解析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

int main
{
	Base b;
	return 0;
}

sizeof(Base)是多少?

想必大部分人第一次做这道题都会觉得是 1,但运行后发现答案是 8

很奇怪,所以我们转到调试查看

发现除了 _b 以外,还多一个 _vfptr 放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针( v 代表 virtualf 代表 function)

通常虚函数都被放在代码段_vfptr 就是虚函数的地址,被存放在虚函数表,虚函数表放在只读数据段,也就是常量区,所以虚函数表本质上是个函数指针数组,虚函数表是在编译期间生成的

✏️那么多个虚函数是怎样实现多态的,举个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}

	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}

	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};

class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Base b;
	Derive d;
	return 0;
}

还是转到监视窗口调试查看:

实际上虚函数表是按照一定规则实现的:

  • 🚩复制基类虚表内容 子类在生成虚表时,首先会把父类虚表中的内容完整地复制一份。这意味着子类虚表初始状态下包含了基类所有虚函数的地址,保证了子类对象可以调用父类的虚函数,这是因为子类继承了基类的接口,在某些情况下可能会使用到基类定义的虚函数实现
  • 🚩重写虚函数的替换 如果子类对父类中的某个虚函数进行了重写,那么在子类虚表中,对应父类虚函数的地址会被替换为子类自己重写后的虚函数地址。当通过父类指针或引用调用该虚函数时,程序会根据对象的实际类型(即子类类型),从子类虚表中找到并重写后的虚函数来执行,从而实现多态性
  • 🚩新增虚函数的添加 对于子类自己新定义的虚函数,会按照它们在子类中声明的先后顺序依次添加到子类虚表的末尾。这些新增的虚函数是子类特有的,父类中并不存在。因此,它们会被单独添加到虚表中,以确保子类对象能够调用这些专属的虚函数

🔥值得注意的是:

  • 父类 b 对象和子类 d 对象虚表是不一样的,这里我们发现 Func1 完成了重写,所以 d 的虚表中存的是重写的 Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法
  • Func2 继承下来后是虚函数,所以放进了虚表,Func3 也继承下来了,但是不是虚函数,所以不会放进虚表
  • 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个 nullptr
  • 一个类的不同对象共享同一个类的虚表

4.2 多态原理实现

那么回归到多态的实现条件:

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

我们可以提出两个问题:

🚩为什么不是子类指针或者引用?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Animal 
{
public:
    virtual void speak() 
    {
        cout << "Animal makes a sound" << endl;
    }
};

class Dog : public Animal 
{
public:
    void speak() override 
    {
        cout << "Dog barks" << endl;
    }
};

class Cat : public Animal 
{
public:
    void speak() override 
    {
        cout << "Cat meows" << endl;
    }
};

int main() {

    Dog dog;
    Animal* animalPtr = &dog;  // 父类指针指向子类对象
    animalPtr->speak();  // 运行时根据实际对象类型调用Dog的speak函数

    Cat cat;
    Animal& animalRef = cat;  // 父类引用绑定到子类对象
    animalRef.speak();  // 运行时根据实际对象类型调用Cat的speak函数

    return 0;
}

这里的子类 DogCat 都继承于父类 Animal,就是因为是父类的指针或引用才能想调用哪个子类都行

如果是子类的指针或引用,比如有个 Dog 类的指针 Dog* dogPtr,它只能指向 Dog 类对象,没办法指向 Cat 类对象。如果想用它去调用 speak 函数,不管怎样都是调用 Dog 类的 speak 函数,不能根据实际对象类型(Cat 或其他子类)来动态调用不同的 speak 函数,就实现不了多态了

🚩为什么不能是父类对象?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票-全价" << endl;
	}
};

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

int main()
{
	Person ps;
	Student st;
	ps = st;
	
	return 0;
}

如果是使用对象,而不是指针或引用,子类中特有的成员变量和函数将被截断,丢失子类的特性

而使用父类指针或引用指向子类对象时,不会发生切片,能够完整保留子类对象的所有信息,从而可以访问子类重写的虚函数以实现多态

🔥值得注意的是: 子类对象赋值给父类对象的时候,不会拷贝虚函数表过去,如果拷贝了,那么父类虚函数表中的虚函数就变成子类虚函数了,就失去多态的意义了

所以总结: 满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中去找的。不满足多态的函数调用时编译时确认好的

4.3 动态绑定与静态绑定

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

动态绑定: 又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态

5.继承和多态常见的面试问题

  1. 下面哪种面向对象的方法可以让你变得富有( ) A: 继承 B: 封装 C: 多态 D: 抽象
  2. ( )是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关, 而对方法的调用则可以关联于具体的对象。 A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定
  3. 面向对象设计中的继承和组合,下面说法错误的是?() A:继承允许我们覆盖重写父类的实现细节,父类的实现对于子类是可见的,是一种静态复用,也称为白盒复用 B:组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动态复用,也称为黑盒复用 C:优先使用继承,而不是组合,是面向对象设计的第二原则 D:继承可以使子类能自动继承父类的接口,但在设计模式中认为这是一种破坏了父类的封装性的表现
  4. 以下关于纯虚函数的说法,正确的是( ) A:声明纯虚函数的类不能实例化对象 B:声明纯虚函数的类是虚基类 C:子类必须实现基类的纯虚函数 D:纯虚函数必须是空函数
  5. 关于虚函数的描述正确的是( ) A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型 B:内联函数不能是虚函数 C:派生类必须重新定义基类的虚函数 D:虚函数可以是一个static型的函数
  6. 关于虚表说法正确的是( ) A:一个类只能有一张虚表 B:基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表 C:虚表是在运行期间动态生成的 D:一个类的不同对象共享该类的虚表
  7. 假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则( ) A:A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址 B:A类对象和B类对象前4个字节存储的都是虚基表的地址 C:A类对象和B类对象前4个字节存储的虚表地址相同 D:A类和B类虚表中虚函数个数相同,但A类和B类使用的不是同一张虚表

参考答案:1. A 2. D 3. C 4. A 5. B 6. D 7. D

  1. 下面程序输出结果是什么? ()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
using namespace std;

class A 
{
public:
	A(const char* s)
	{ 
		cout << s << endl; 
	}

	~A() 
	{}
};

class B :virtual public A
{
public:
	B(const char* s1, const char* s2)
		:A(s1) 
	{ 
		cout << s2 << endl; 
	}
};

class C :virtual public A
{
public:
	C(const char* s1, const char* s2)
		:A(s1) 
	{ 
		cout << s2 << endl; 
	}
};

class D :public B, public C
{
public:
	D(const char* s1, const char* s2, const char* s3, const char* s4)
		:B(s1, s2)
		, C(s1, s3)
		, A(s1)
	{
		cout << s4 << endl;
	}
};

int main() 
{
	D* p = new D("class A", "class B", "class C", "class D");
	delete p;
	return 0;
}

A:class A class B class C class D B:class D class B class C class A C:class D class C class B class A D:class A class C class B class D

解析: 这是个菱形虚拟继承,所以 A 只会被调用一次,D 类里的初始化列表是按声明的顺序来初始化的,所以按 ABCD 的顺序,因此答案选 A

  1. 多继承中指针偏移问题?下面说法正确的是( )
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Base1 
{ 
public:
	int _b1; 
};

class Base2 
{ 
public:
	int _b2; 
};

class Derive : public Base1, public Base2 
{ 
public: 
	int _d; 
};

int main() 
{
	Derive d;
	Base1* p1 = &d;
	Base2* p2 = &d;
	Derive* p3 = &d;
	return 0;
}

A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3

解析: 画图理解即可,选 C

  1. 以下程序输出结果是什么()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A
{
public:
	virtual void func(int val = 1) 
	{ 
		cout << "A->" << val << endl; 
	}

	virtual void test() 
	{ 
		func(); 
	}
};

class B : public A
{
public:
	void func(int val = 0) 
	{ 
		cout << "B->" << val << endl; 
	}
};

int main(int argc, char* argv[])
{
	B* p = new B;
	p->test();
	return 0;
}

A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确

解析: 这题绝大多数人肯定会选到 D,这题的知识点确实比较偏,首先我们要知道多态重写的是实现,即只有 {} 内的内容是多态的,实际上子类的函数头其实相当于是从父类拷贝过来的,因此函数头的内容还是调用的父类的,所以答案选 B

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
恭喜您!您在【热点技术征文第十期1024程序员节】征文活动中获奖啦!获奖名单详情请查看:https://cloud.tencent.com/developer/article/2457705 请您抽取几分钟填写收货信息问卷以领取奖励。问卷链接:https://cloud.tencent.com/apply/p/xf9huqg09j *问卷填写日期截止2024年12月16日,请注意截止时间尽快填写。
恭喜您!您在【热点技术征文第十期1024程序员节】征文活动中获奖啦!获奖名单详情请查看:https://cloud.tencent.com/developer/article/2457705 请您抽取几分钟填写收货信息问卷以领取奖励。问卷链接:https://cloud.tencent.com/apply/p/xf9huqg09j *问卷填写日期截止2024年12月16日,请注意截止时间尽快填写。
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
谷歌68页白皮书解密:提示工程如何重塑AI交互逻辑
谷歌发布的《提示工程白皮书》揭示了一个重要趋势:人类与AI的交互正从“指令式问答”升级为“思维协同编程” 。数据显示 ,优化后的提示设计可使LLM生成结果的准确率提升58%,代码生成效率提高120% 。
山河已无恙
2025/04/15
2920
谷歌68页白皮书解密:提示工程如何重塑AI交互逻辑
99%的普通人不会用的AI沟通术:揭秘我与AI对话的沟通技巧
Prompt Engineering(提示工程)作为与 LLM 交互的关键技术,正逐渐成为连接人类需求与模型能力的桥梁。
AIGC新知
2025/04/13
1360
99%的普通人不会用的AI沟通术:揭秘我与AI对话的沟通技巧
10种实用的Prompt技巧图解
收集整理了prompt engineering的10种实用技巧,以图解的方式解释了它们的主要原理。
lyhue1991
2023/09/05
2.7K0
10种实用的Prompt技巧图解
迈向大模型第一步:Prompt提示工程基础原理及实践
想象一下,你在装饰房间。你可以选择一套标准的家具,这是快捷且方便的方式,但可能无法完全符合你的个人风格或需求。另一方面,你也可以选择定制家具,选择特定的颜色、材料和设计,以确保每件家具都符合你的喜好和空间要求。
fanstuck
2024/01/17
3.7K3
迈向大模型第一步:Prompt提示工程基础原理及实践
Prompt工程师指南从基础到进阶篇:用于开发和优化提示,以有效地使用语言模型(LMs)进行各种应用和研究主题
Prompt工程是一种相对较新的学科,用于开发和优化提示,以有效地使用语言模型(LMs)进行各种应用和研究主题。Prompt工程技能有助于更好地理解大型语言模型(LLMs)的能力和局限性。研究人员使用Prompt工程来改善LLMs在各种常见和复杂任务上的能力,
汀丶人工智能
2023/05/14
3.7K1
Prompt工程师指南从基础到进阶篇:用于开发和优化提示,以有效地使用语言模型(LMs)进行各种应用和研究主题
Prompt Learning:【文心一言】提示词功能系统学习,
大型语言模型使用强化学习中的人类反馈来学习,这个过程中与人类对话的提问通常是通俗易懂的,也就是说,大型语言模型可以理解并回答一般人能听懂的问题(可以看到,后面我们所提到的技巧,用到我们日常的交流中也是可以更清晰的表达自己的目的的)。因此,即使我们不进行prompt学习,也可以使用大型语言模型来帮助我们解决问题了。但是,如果想要解决更加专业,更加复杂的问题,就需要我们学习prompt工程,以便更好的让大型语言模型来输出我们想要的结果。
汀丶人工智能
2023/04/22
1.8K0
Prompt Learning:【文心一言】提示词功能系统学习,
提升ChatGPT性能的实用指南:Prompt Engineering的艺术
提示工程是一门新兴学科,就像是为大语言模型(LLM)设计的"语言游戏"。通过这个"游戏",我们可以更有效地引导 LLM 来处理问题。只有熟悉了这个游戏的规则,我们才能更清楚地认识到 LLM 的能力和局限。
腾讯技术工程官方号
2023/08/11
1.7K0
提升ChatGPT性能的实用指南:Prompt Engineering的艺术
提示词(prompt)工程指南(一):提示介绍
提示工程是一种相对较新的学科,专门用于开发和优化提示,以高效地使用语言模型(LM)来处理各种应用和研究主题。提示工程技能有助于更好地理解大型语言模型(LLMs)的能力和局限性。研究人员使用提示工程来提高LLMs在各种常见和复杂任务上的容量,例如问题解答和算术推理。开发人员使用提示工程来设计与LLMs和其他工具接口的强大而有效的提示技术。
云微
2023/03/31
2.3K0
【AI大模型】ChatGPT模型原理介绍(下)
2020年5月, OpenAI发布了GPT-3, 同时发表了论文“Language Models are Few-Shot Learner”《小样本学习者的语言模型》.
小言从不摸鱼
2024/09/18
2110
【AI大模型】ChatGPT模型原理介绍(下)
提示词(prompt)工程指南(三):高级提示
到此为止,已经很明显完善提示有助于在不同任务上获得更好的结果。这就是提示工程的整体理念。
云微
2023/03/31
1.7K0
解读提示工程(Prompt Engineering)
提示工程(Prompt Engineering),也称为上下文提示,是一种通过不更新模型的权重/参数来引导LLM行为朝着特定结果的方法。这是与AI有效交流所需结果的过程。提示工程可以用于各种任务,从回答问题到算术推理乃至各种应用领域,理解提示工程,能够帮助我们了解LLM的限制和能力。
半吊子全栈工匠
2023/10/23
8.8K0
解读提示工程(Prompt Engineering)
介绍提示学习、连续提示、离散提示
2021 年,提示学习(Prompt Learning)的研究浪潮兴起。而早在 2020 年,OpenAI 在 NeurIPS 2020 发表的一篇论文 Language Models are Few-Shot Learners 中就已经探讨了如何利用提示学习来提升大语言模型(Large Language Models,LLMs)的推理能力。论文中介绍了 Zero-shot、One-shot、Few-shot 三种不同的提示方法,如下图示意。
叶庭云
2024/05/25
6730
介绍提示学习、连续提示、离散提示
从大模型的原理到提示词优化
  大语言模型(后文简称LLM)已经火热了近两年,相信大家或多或少都学习过一些Prompt技巧。但你有没有思考过:为什么使用这些Prompt能得到更好的输出结果?为什么有时你怎么试都得不到想要的结果?为什么有些任务LLM根本无法完成?接下来,我们通过一个简单示例来了解LLM的运行过程。理解这个过程后,你自然就能回答上述问题了。
xindoo
2024/10/27
3530
从大模型的原理到提示词优化
Prompt Engineering:提示词工程最佳实践与应用
随着人工智能技术的不断发展,特别是大型语言模型(LLM)的兴起,提示词工程(Prompt Engineering)逐渐成为提升AI模型性能、增强任务执行效率的关键技术。Prompt Engineering通过设计和优化提示词(Prompt),引导LLM生成特定类型的输出,广泛应用于文本生成、数据增强、代码生成、问答系统等领域。本文将深入探讨Prompt Engineering的背景知识、功能点、优点、缺点,以及与市场其他技术的对比,并结合具体业务场景和底层原理,通过Java示例展示其实际应用。
小马哥学JAVA
2024/12/26
8170
纯干货!最全Prompt工程方法总结,与ChatGPT、GPT-4等LLMs的交互更高效!
 随着生成式人工智能(尤其是ChatGPT、GPT-4)的爆炸性普及,对于人工智能领域的人们来说,写Prompt已经成为了一项越来越重要的技能。然而,当您在实操过程中会发现,并不是乍看起来那么简单的语法任务。当体验完ChatGPT、GPT-4等大模型的新鲜感之后,写Prompt需要个人练习和思考才能快速掌握该技能。因此,根据实际应用场景创建最有效的Prompt(Prompt工程)已经成为LLMs领域内外令人垂涎的专业知识。这也催生出了Prompt工程师的岗位。
ShuYini
2023/09/14
9.1K0
纯干货!最全Prompt工程方法总结,与ChatGPT、GPT-4等LLMs的交互更高效!
使用 ChatGPT 的 7 个技巧 | Prompt Engineering 学习笔记
前段时间在 DeepLearning 学了一门 Prompt 的课程,吴恩达本人授课,讲的通俗易懂,感觉受益匪浅,因此在这里总结分享一下我的学习笔记。
phoenix.xiao
2023/08/28
6240
使用 ChatGPT 的 7 个技巧 | Prompt Engineering 学习笔记
​解密prompt系列5. APE+SELF=自动化指令集构建代码实现
上一章我们介绍了不同的指令微调方案, 这一章我们介绍如何降低指令数据集的人工标注成本!这样每个人都可以构建自己的专属指令集, 哈哈当然我也在造数据集进行时~
风雨中的小七
2023/04/09
3.4K0
​解密prompt系列5. APE+SELF=自动化指令集构建代码实现
MLLM首篇综述 | 一文全览多模态大模型的前世、今生和未来
近年来,大型语言模型取得了显著进展。通过扩大数据大小和模型大小,这些LLM提高了惊人的涌现能力,通常包括上下文学习(ICL)、指令跟随和思想链(CoT)。尽管LLM在大多数自然语言处理(NLP)任务中表现出了令人惊讶的Zero/Few-Shot推理性能,但它们天生对视觉“视而不见”,因为它们只能理解离散文本。
集智书童公众号
2023/09/04
13.3K0
MLLM首篇综述 | 一文全览多模态大模型的前世、今生和未来
大模型Prompt-Tuning技术进阶
近两年来,随之Prompt-Tuning技术的发展,有诸多工作发现,对于超过10亿参数量的模型来说,Prompt-Tuning所带来的增益远远高于标准的Fine-tuning,小样本甚至是零样本的性能也能够极大地被激发出来,得益于这些模型的 参数量足够大 ,训练过程中使用了 足够多的语料 ,同时设计的 预训练任务足够有效 。最为经典的大规模语言模型则是2020年提出的GPT-3,其拥有大约1750亿的参数,且发现只需要设计合适的模板或指令即可以 实现免参数训练的零样本学习 。
@小森
2024/06/06
3800
大模型Prompt-Tuning技术进阶
来啦来啦!关于CoT提示策略综述
思维链(CoT)是一个循序渐进、连贯的推理链,通常被用作大型语言模型(LLM)的提示策略并体现出了巨大的优势。近年来,基于CoT提示的展示出的效果吸引了众多的研究。
zenRRan
2023/10/14
1.2K0
来啦来啦!关于CoT提示策略综述
推荐阅读
相关推荐
谷歌68页白皮书解密:提示工程如何重塑AI交互逻辑
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验