首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

具有operator =重载代码的简单用户定义类崩溃

是指在重载了赋值运算符(operator=)的用户定义类中,当进行对象赋值操作时,程序出现错误导致崩溃。

在C++中,赋值运算符(operator=)用于将一个对象的值赋给另一个对象。当用户定义类中没有显式定义赋值运算符时,编译器会生成一个默认的赋值运算符,该默认赋值运算符执行浅拷贝操作,即简单地将一个对象的成员变量的值复制给另一个对象的对应成员变量。

然而,当用户定义类中存在动态分配的资源(如堆内存)或者有指针成员变量时,简单的浅拷贝操作可能会导致问题。这时,我们需要重载赋值运算符,以实现深拷贝或者其他适当的操作,确保对象赋值的正确性和安全性。

然而,如果在重载赋值运算符时出现错误,比如未正确释放资源、未处理异常等,就可能导致程序崩溃。常见的错误包括:

  1. 浅拷贝导致的资源重复释放或内存泄漏:如果在赋值运算符中简单地将指针成员变量进行赋值,那么两个对象将指向同一块内存,当其中一个对象析构时,会释放该内存,导致另一个对象的指针成员变量变为悬空指针,访问该指针将导致崩溃。
  2. 异常安全性问题:如果在赋值运算符中发生异常,而没有正确处理异常或回滚操作,可能导致资源未释放或对象状态不一致,进而导致崩溃。

为避免这些问题,我们应该在重载赋值运算符时进行以下操作:

  1. 首先,判断是否为自赋值情况,如果是,则直接返回当前对象。
  2. 然后,释放当前对象已有的资源,确保不会出现资源泄漏。
  3. 分配新的资源,并进行深拷贝,确保对象的独立性。
  4. 处理异常安全性,确保在发生异常时能够正确处理并回滚操作。

以下是一个示例代码,演示了如何重载赋值运算符以避免崩溃:

代码语言:txt
复制
class MyClass {
public:
    MyClass() : data(nullptr) {}
    MyClass(const MyClass& other) {
        // 深拷贝构造函数
        data = new int(*other.data);
    }
    ~MyClass() {
        delete data;
    }
    MyClass& operator=(const MyClass& other) {
        if (this == &other) {
            return *this; // 自赋值情况,直接返回当前对象
        }
        delete data; // 释放已有资源
        data = new int(*other.data); // 深拷贝
        return *this;
    }
private:
    int* data;
};

在上述示例中,我们通过重载赋值运算符,实现了深拷贝操作,确保了对象赋值的正确性和安全性。

腾讯云相关产品和产品介绍链接地址:

  • 云服务器(CVM):提供弹性计算能力,可快速创建、部署和扩展云服务器。
  • 云数据库 MySQL:提供高性能、可扩展的关系型数据库服务,适用于各种应用场景。
  • 云存储(COS):提供安全可靠、低成本的对象存储服务,适用于海量数据存储和访问。
  • 人工智能:提供丰富的人工智能服务,包括图像识别、语音识别、自然语言处理等。
  • 物联网(IoT):提供全面的物联网解决方案,帮助用户快速构建和管理物联网设备。
  • 区块链(BCBaaS):提供安全可信的区块链服务,支持快速搭建和管理区块链网络。
  • 音视频处理:提供音视频处理和分发服务,包括转码、截图、水印、直播等功能。
  • 移动开发(移动推送):提供移动应用推送服务,帮助开发者实现消息推送和用户管理。
  • 云原生:提供云原生应用开发和管理的解决方案,支持容器化部署和微服务架构。
  • 网络安全(Web 应用防火墙):提供全面的网络安全服务,保护 Web 应用免受攻击。
  • 多媒体处理:提供多媒体处理和分发服务,包括音视频转码、截图、水印、直播等功能。
  • 数据库(TDSQL):提供高性能、可扩展的关系型数据库服务,适用于各种应用场景。
  • 元宇宙:提供元宇宙解决方案,支持虚拟现实、增强现实等应用场景。

请注意,以上仅为示例产品,具体选择应根据实际需求进行评估和决策。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

【C++和对象】拷贝构造与赋值运算符重载

d1; Date d2; d1 == d2;//我们想要进行日期对象进行判断是否相等就需要对运算符进行重载 return 0; } C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数...不能通过连接其他(不是运算符)符号来创建新操作符:比如operator@ 。 重载操作符必须有一个类型参数(不能全是内置类型,不然就不是重载运算符了)。...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数。...3.默认生成赋值运算符重载 在C++和对象中用户没有显式实现赋值运算符重载时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝; 注意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值...,而此时默认生成函数对于内置类型会进行浅拷贝,对于自定义类型则会调用它拷贝构造函数或赋值运算符重载; 所以如果是简单日期中未涉及到资源管理,就可以使用编译器默认生成函数,对于含有指针或动态分配资源比如栈就不能依靠编译器要自己显式实现对应函数

16110

【C++干货基地】特殊函数名函数:赋值运算符重载

一、运算符重载 C++ 中为了增加代码可读性运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。...< 运算符重载就更加简单了,和 运算符 == 规则一样只需要改变一下我们成员比较方法就可以了 代码演示: class Date { public: Date(int year, int...前面的几个赋值运算符重载比较简单,只是为了完成是如何比较而赋值运算符就是我们这里比较关键了 赋值运算符重载 主要是赋值,把一个对象赋值给另一个对象 而 拷贝构造函数 主要是,同类型对象创建初始化时调用...注:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。 2.4 有了自动生成赋值重载我们还需要自己写吗?...s1 所指向地址给以值方式赋值了,所以 s2 s1 指向是同一块空间当他们自动调用析构函数时就会对同一块地址释放俩次导致程序崩溃 三、前置++ 和 后置++ 前面我们重载都是二元运算符,看似只有一个显示定义参数其实还有一个

5900
  • C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载与对象_中篇)

    赋值运算符重载 运算符重载 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。...函数名字为:关键字operator后面接需要重载运算符符号。...函数原型:返回值类型 operator操作符(参数列表) 注意: 不能通过连接其他符号来创建新操作符:比如operator@ 重载操作符必须有一个类型参数 用于内置类型运算符,...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数。 3....用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝。 注意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。

    8100

    C++从入门到精通——6个默认成员函数之赋值运算符重载

    class Date {}; 一、运算符重载 定义 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似...增加灵活性:无论是函数重载还是运算符重载,都可以根据需要定义不同行为,使得代码更加灵活易用。...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数。...用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝。 注意: 内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。...当然像日期这样是没必要。那么下面的呢?验证一下试试? // 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲深拷贝去解决。

    11910

    与对象中篇

    代码可以通过编译,因为编译器生成了一个无参默认构造函数 //将Date中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成    //放开后报错:error C2512...当然像日期这样是没必要,那么下面的呢? //这里会发现下面的程序会崩溃掉? 崩溃原因:同一块空间析构两次 需要用深拷贝去解决。...5.赋值运算符重载 运算符重载: C++为了使自定义类型也能使用运算符同时增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型和参数列表与普通函数类似..._day; } return left; } // 编译失败: // error C2801: “operator =”必须是非静态成员 原因:赋值运算符如果不显示实现,编译器会生成一个默认,此时用户再在外自己实现一个全局赋值运算符重载...用户没有显示实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝。注意:内置类型成员是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。

    51830

    【C++】和对象(中)

    ++编译器会自动生成一个无参默认构造函数,一旦用户显式定义编译器将不再生成 。...程序最终会崩溃,同一块空间析构了两次(st2先析构)。这是浅拷贝,对于一些可以,对于一些不可以。此时很明显,默认生成拷贝构造函数已经不能符合我们要求了。...(比如上面的栈),都需要写深拷贝拷贝构造 对于不需要写析构函数(比如我们一直接触日期Date),默认生成浅拷贝构造函数就可以了 ---- 五、赋值运算符重载 1.运算符重载 C++为了增强代码可读性引入了运算符重载...,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。...&) 检测是否自己给自己赋值 返回*this 一个如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序值拷贝。

    1.2K30

    【C++】拷贝构造函数和赋值运算符重载详解

    编译器生成默认拷贝构造函数已经可以完成字节序值拷贝了,还需要自己显式实现吗? 当然像日期这样是没必要。那么下面的呢?验证一下试试? // 这里会发现下面的程序会崩溃掉?...⭐运算符重载 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数。...用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝。 注意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符 重载完成赋值。...当然像日期这样是没必要。那么下面的呢?验证一下试试? // 这里会发现下面的程序会崩溃掉?这里就需要深拷贝去解决。

    19710

    C++IO流详解

    特性是:有序连续、具有方向性 为了实现这种流动, C++定义了 I/O 标准库,这些每个都称为流/流,用以完成某方面的功能 Ⅲ...._age); return 0; } 这段代码看上去没有什么问题,但如果我们第二次输入字符串过长,会导致程序崩溃,原因如下 : string内部会有一个 _Buf数组,当存储字符串大小小于...对于 自定义类型,如果要支持cin和cout标准输入输出,需要对>进行重载。..._day ; return out; } // C++ IO流,使用面向对象+运算符重载方式 // 能更好兼容自定义类型,流插入和流提取 int main() { // 自动识别类型本质...,都使用 // 一样方式,去流插入和流提取数据 // 当然这里自定义类型Date需要重载>> 和 << // istream& operator >> (istream& in

    60230

    C++四个默认函数(构造函数,析构函数,拷贝函数,赋值函数)

    在C++中,对于一个,C++编译器都会为这个提供四个默认函数,分别是: A() //默认构造函数 ~A() //默认析构函数 A(const A&) //默认拷贝构造函数 A& operator...构造函数功能是由用户定义用户根据初始化要求设计函数体和函数参数,可以是一个,也可以是多个,可以把构造函数理解为重载一种(函数名相同,不会返回任何类型,也不可以是void类型,参数类型个数可不同...而且发现当重复释放两个指针分别属于两个或者说是两个变量时候,会发生崩溃,如果对一个变量多次释放则不会崩溃。...,一个自定义类型对象,如果想要进行预期加减乘除之类运算,或者是像内置类型一样,用cout输出一个对象,这些都是需要我们来用代码告诉机器怎么做,都是需要我们来指定。...class Animal { //中间代码略 friend ostream& operator << (ostream& os, Animal& a) { os << *a.name << "

    2.2K20

    C++心决之类和对象详解(中篇)(封装入门二阶)

    如果类中没有显式定义构造函数,则 C++ 编译器会自动生成一个无参默认构造函数,一旦 用户显式定义编译器将不再生成。...,代码可以通过编译,因为编译器生成了一个无参默认构造函 数 // 将Date中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再 生成 // 无参构造函数,放开后报错...5.赋值运算符重载 5.1 运算符重载 C++ 为了增强代码可读性引入了运算符重载 , 运算符重载具有特殊函数名函数 ,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似...用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝 。注 意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符 重载完成赋值。...当然像日期这样是没必要。那么下面的呢? // 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲深拷贝去解决。

    8210

    【C++修行之道】和对象(四)运算符重载

    一、 运算符重载 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。...成员函数不仅包含函数代码,还隐含地包含了一个指向对象this指针,这使得成员函数能够访问和修改对象状态。...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数。 3....用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝。 注意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。...当然像日期这样是没必要。那么下面的呢?验证一下试试? // 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲深拷贝去解决。

    9210

    【C++】C++入门—初识构造函数 , 析构函数,拷贝构造函数,赋值运算符重载

    ,C++编译器会自动生成一个无参默认构造函数,一旦用户显示定义,编译器将不在生成。...拷贝构造函数典型调用场景: 使用已存在对象创建新对象 函数参数类型为类型对象 函数返回值类型为类型对象 4 赋值运算符重载 运算符重载 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数...函数原型:返回值类型 operator"操作符" (参数列表) 注意: 不能通过连接其他符号来创建新操作符:比如operator@ 重载操作符必须有一个类型参数 用于内置类型运算符,其含义不能改变...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝...注意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。 既然编译器生成默认赋值运算符重载函数已经可以完成字节序值拷贝了 还需要自己实现吗?

    15810

    【C++】掌握C++六个默认成员函数:实现高效内存管理与对象操作

    命名空间 缺省参数与函数重载 C++相关知识 和对象上 引言 本篇为大家分享下在C++学习中较为具有挑战与难度,同时也是很重要知识。...一、六个默认成员函数 默认成员函数是指用户没有显式实现,编译器会自动生成成员函数称为默认成员函数。...5.1 运算符重载函数概念 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数(不用我们取名字),也是具其返回值类型,函数名字以及参数列表,其返回值类型与参数列表于普通函数类似...,编译器会生成一个默认赋值运算符重载,此时用户外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突,故而赋值运算符只能是成员函数(其他运算符函数可以重载为全局函数)...特性:用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝 默认生成赋值运算符重载对于内置类型与自定义类型处理方式 内置类型成员变量直接赋值定义类型成员变量需要调用对应赋值运算符重载完成赋值

    10500

    【C++初阶】与对象:6大默认成员函数------拷贝构造和赋值运算符重载

    ; 3.函数返回值类型为类型对象; 一般传参数是自定义类型时,适合用引用接收; 返回值是否用引用,要看返回值类型是否会被销毁; 二.运算符重载 C++为了增强代码可读性引入了运算符重载:...运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似; 函数名字为:关键字operator后面接需要重载运算符符号; 函数原型:返回值类型...注意: 1.不能通过连接其他符号来创建新操作符:比如operator@; 2.重载操作符必须有一个类型参数; 3.用于内置类型运算符,其含义不能改变,例如:内置整型+,不 能改变其含义;...3.返回*this :要符合连续赋值含义; 注意: 赋值运算符只能重载成员函数不能重载成全局函数,因为重载成全局里面又会自动生成一个,这样编译器就无法分辨; 如果没有显示定义赋值运算符重载...,那么会自动生成赋值运算符重载,这和拷贝构造函数类似: 1.对内置类型完成浅拷贝; 2.对自定义类型会去调用它赋值运算符重载函数 下面是日期赋值运算符重载: Date& operator

    13510

    从基础入门到学穿C++(和对象篇)【超详细】【一站式速通】

    程序中对象具有状态(属性)和行为(方法),能够封装数据和功能。代码(Class)和对象(Object)为单位进行组织,将相关数据和功能封装在一起,以提高代码可维护性和复用性。...(简单来说就是传参过程又是一个拷贝构造调用)编译器会直接检查出来这种错误。...C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数....函数原型 返回值类型 operator操作符(参数列表) 运算符有几个操作数,operator重载函数就有几个参数。...例如下面代码,我们在实现string时候,如果想要直接把string对象使用cout输出就需要重载<<操作符,因此我们写出了下面代码. ostream& operator<<(ostream& out

    8710

    CC++对bool operator < (const p &a)const认识,运算符重载详解(杂谈)

    但实际上,对于许多用户定义类型(例如),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋予已有运算符新功能,使它能够用于特定类型执行特定操作。...(5) 运算符重载不能改变该运算符用于内部类型对象含义。它只能和用户定义类型对象一起使用,或者用于用户定义类型对象和内部类型对象混合使用时。...但成员函数运算符与友元函数运算符也具有各自一些特点: (1) 一般情况下,单目运算符最好重载成员函数;双目运算符则最好重载友元函数。...成员函数形式比较简单,就是在里面定义了一个与操作符相关函数。友元函数因为没有this指针,所以形参会多一个。...运算符重载不能改变该运算符用于内部类型对象含义。它只能和用户定义类型对象一起使用,或者用于用户定义类型对象和内部类型对象混合使用时。

    2.2K61

    C++初阶与对象(三):详解复制构造函数和运算符重载

    再次销毁必然崩溃 看到c++里值拷贝是有风险(默认拷贝),所以解决方案: 规定,自定义类型对象拷贝时候,调用一个函数,这个函数就叫拷贝构造函数 1.2特性 拷贝构造函数也是特殊成员函数...函数返回值类型为类型对象 2.赋值运算符重载 2.1运算符重载 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝...注意:内置类型成员变量是直接赋值(浅拷贝),而自定义类型成员变量需要调用对应赋值运算符重载完成赋值(跟拷贝构造行为类似,内置类型值拷贝,自定义类型调用他赋值) 既然编译器生成默认赋值运算符重载函数已经可以完成字节序值拷贝了...std::ostream是一个抽象,它不能被直接实例化 std::cout是标准输出流对象,已经在标准库中定义好了,并且支持输出运算符<<重载,所以传入参数就好了 3.const成员 将const

    17010

    【c++】和对象(五)赋值运算符重载

    1.赋值运算符重载 1.1运算符重载 运算符重载是一种编程语言特性,它允许开发者为已有的运算符提供自定义实现。这意味着你可以改变某些运算符在你自定义或数据类型上行为。...这里就用到了操作符重载 运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似,注意这里说重载与我们函数重载不是一个意思 函数名字为...,我们就返回int类型,判断是否大于小于,就用bool类型 1.1.1特性: 不能通过连接其他符号来创建新操作符:比如operator@ 重载操作符必须有一个类型参数(自定义类型参数) 用于内置类型运算符...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数 如果我们不写赋值运算符重载,编译器是否会默认生成呢?...1.4前置++和后置++重载 在C++中,前置++和后置++运算符都可以被重载,以提供用户定义类型(比如自增功能。

    17910

    C++奇迹之旅:深入理解赋值运算符重载

    通过运算符重载,我们可以使用与内置数据类型相同语法来操作自定义类型,从而提高代码可读性和可维护性。...,能不能直接通过像内置类型那样d1==d2来比较相同呀,因此运算符重载就来了: C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数。...用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝。注意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。...当然像日期这样是没必要。那么下面的呢?验证一下试试? // 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲深拷贝去解决。

    12110

    【C++ 初阶路】--- 和对象(下)

    // 这里会发现下面的程序会崩溃掉?...三、赋值运算符重载 3.1 运算符重载 C++为了增强代码可读性引入了运算符重载,运算符重载具有特殊函数名函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通函数类似。...函数原型:返回值类型 operator操作符(参数列表) 注意: 不能通过连接其他符号来创建新操作符:比如operator@ 重载操作符必须有一个类型参数 用于内置类型运算符,其含义不能改变,例如...此时用户再在外自己实现一个全局赋值运算符重载,就和编译器在中生成默认赋值运算符重载冲突了,故赋值运算符重载只能是成员函数。...用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值方式逐字节拷贝。 注意:内置类型成员变量是直接赋值,而自定义类型成员变量需要调用对应赋值运算符重载完成赋值。

    7310
    领券