c++基础学习2:类和对象

1. 认识类与对象

  • 什么是类(class)?
    类(class)是类型(type),是用户自定义的类型。为什么不叫它type,因为借用Simula语言中的class关键字。

  • 为什么要有类?
    基于便利性的考虑,现实世界中物(object)通常被分为几种独立的分类。

  • 基本概念

概念

比喻

对象/实例

楼房

实例化

建造

扫描二维码关注公众号,回复: 9188033 查看本文章

建筑图纸

  • 面向对象四大特征

特征

说明

类比

抽象

抽出具体事物的普遍性的本质

分门别类:鸟类、哺乳类、鱼类

封装

把数据与处理(函数)包在一起

通信录(增加、删除)

继承

数据与处理(函数)的传承

财富与绝技、混血儿(肤色/头发、 两种语言

多态

同一个事物(函数)的多种形态

手机键盘数字与字母、 电脑键盘功能键

2. 类的定义与对象创建

2.1 类的定义:与struct相似(C++)
class 类名{
    成员变量成员函数声明
};

class定义最后的;一定不要忘记。

  • 构成

构成

作用

数据成员(data member)/成员变量/属性

对象内部数据和状态,只能在类定义中声明,可以在成员函数中直接调用。

成员函数/方法

对象相关的操作,可以在类内实现或类外实现。

  • 实例:复数Complex
  • 作用域运算符:: -- 函数归属
  • 访问限定符

限定符

作用

private[默认]

私有

public

公开

protected

保护

实践中,成员变量多数情况使用private或者protected,成员函数多数情况使用public。通常,通过成员函数改变对象的成员变量。

  • 类定义与类实现分离
  • 头文件 – 声明
    方式:#pragma once或者#ifnde...#endif
    作用:防止头文件二次编译
  • 源文件 -- 实现
    引用头文件:#include <>(标准库函数)/#include ""(自定义/第三方函数)

在C++书籍中为了方便.h.cpp不做分离,但是项目开发中,需要分开。

  • 强化练习:账单Bill(名称、数量、单价、小计)
  • vector与string

  • classstruct区别
    struct_class.cpp

    #include

    using std::cout;
    using std::endl;

    struct SPos {
    int x,y,z;
    };
    class CPos {
    int x,y,z;
    };
    int main(){
    #ifdef STRUCT
    SPos spos = {1,1,1};
    cout << “(” << spos.x << “,” << spos.y << “,” << spos.z << “)” << endl;
    #else
    CPos cpos = {1,1,1};
    cout << “(” << cpos.x << “,” << cpos.y << “,” << cpos.z << “)” << endl;
    #endif
    }

C++classstruct区别是:

  1. 默认的访问控制不同
    structpublicclassprivate
  2. struct可以使用花括号内的初始值列表{...}初始化,class不可以(C++98不可以,C++11可以)。

注意:

  • C++的struct可以有成员函数,而C不可以。
  • C++的struct可以使用访问控制关键字(public private protected),而C不可以。
  • C++的struct
SPos spos; // C++
struct SPos spos; // C/C++

成员变量默认初始化为随机值(主要影响指针)。

2.2 对象创建/实例化
直接创建 -- 类作为类型定义变量 -- 栈上创建
  • 例如:

基本类型

int main(){
    int a = 10;
    int b(10);
    cout << a << " " << b << endl;
}

基本类型的初始化新增语法:

int a(0);// 等价 int a = 0;
const float b(1.0);// 等价 const float b = 1.0;

类类型

// 定义类
class Demo{};
// 创建对象
int main(){
    Demo d; // 变量(命名对象)
    Demo(); // 匿名对象
}
  • 基本语法

    类名 对象名; // 调用默认构造函数
    类名(); // 创建匿名对象

动态创建 -- 堆上创建
  • 例如

基本类型

int* p = new int;
delete p;
p = NULL;

类类型

// 定义类
class Demo{};
// 创建对象
int main(){
    Demo* d = new Demo;
    delete d;
    d = NULL;
}
  • 基本语法

    类名* 对象指针 = new 类名;// 调用默认构造函数
    delete 对象指针;

对象指针new可以为对象设置初始值,例如下面代码

int* p = new int(100);
cout << *p << endl;
动态创建数组 -- 堆上创建
  • 例如

基本类型

int* pa = new int[10];
delete pa;// 只释放p[0]
delete [] pa;// 释放全部数组

类类型

// 定义类
class Demo{};
// 创建对象
int main(){
    Demo* d = new Demo[10];
    delete [] d;
    d = NULL;
}

对象数组指针new不可以为对象设置初始值。

int* pa = new int[10](100); // error: array 'new' cannot have initialization arguments 

注意:C++除了特殊情况,很少直接使用malloc()/free()申请释放内存,取而代之的是new/delete

强化练习:银行与账户

空结构体与空类的大小(sizeof)为1,主要在于初始化/实例化时,编译器给变量/对象分配内存(地址),内存最小单位为1个字节。
通常,sizeof(类型) == sizeof(变量)


3. this指针

  • 作用域:类内部
  • 特点
    • 类的一个自动生成、自动隐藏的私有成员
    • 每个对象仅有一个this指针
    • 当一个对象被创建时,this指针就存放指向对象数据的首地址
    • 不是对象本身的一部分,不会影响sizeof(对象)的结果

如果成员函数形参与成员变量同名,使用this->做为前缀区分。

4. 方法

4.1 构造函数
1. 语法
类名(参数){
    函数体
}
特点
  • 在对象被创建时自动执行
  • 构造函数的函数名与类名相同
  • 没有返回值类型、也没有返回值
  • 可以有多个构造函数
调用时机
  • 对象直接定义创建–构造函数不能被显式调用
  • new动态创建
默认构造函数

类中没有显式的定义任何构造函数,编译器就会自动为该类型生成默认构造函数,默认构造函数没有参数。

构造函数的三个作用
  • 给创建的对象建立一个标识符
  • 为对象数据成员开辟内存空间
  • 完成对象数据成员的初始化
初始化列表
语法
类名(参数):成员变量(参数){
  函数体
}
作用

初始化非静态成员变量

说明

从概念上来讲,构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。

  • 必须使用初始化列表的情况
    1.常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面。
    2.引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面。
    3.没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
  • 初始化列表与构造函数内部成员变量赋值的区别
    成员变量初始化与成员变量赋值

能使用初始化列表的时候尽量使用初始化列表

强化练习:银行管理多个账户

成员变量的初始化顺序
#include <iostream>
using std::cout;
using std::endl;
class Member1{
public:
    Member1(){
        cout << "Member1 Init" <<endl;
    }   
};
class Member2{
public:
    Member2(){
        cout << "Member2 Init" <<endl;
    }   
};
class Member3{
public:
    Member3(){
        cout << "Member3 Init" <<endl;
    }   
};
class Test{
public:
    Test():m3(),m2(),m1(){};
private:
    Member1 m1;
    Member2 m2;
    Member3 m3;
};
int main(){
    Test test;
}

成员变量在使用初始化列表初始化时,与构造函数中初始化成员列表的顺序无关,只与定义成员变量的顺序有关。

C++的函数可以增加默认参数。

写法:

  1. 默认参数必须写在函数声明中,不能写在函数实现的参数列表中中。
  2. 默认参数必须写在所有非默认参数的后面。
  3. 默认参数可以写任意多个。

使用:

  • 默认参数可以不用传递值,此时,使用默认值。
  • 默认参数可以传值,此时,使用实参值。

扩展阅读


4.2 析构函数

问题:银行管理多个账户的内存泄露及解决方案

语法:
~类名(){
    函数体
}
  • 析构函数的函数名与类名相同
  • 函数名前必须有一个~
  • 没有参数
  • 没有返回值类型、也没有返回值
  • 只能有一个析构函数
调用时机
  • 对象离开作用域
  • delete
默认析构函数

类中没有显式的定义析构函数,编译器就会自动为该类型生成默认析构函数

作用

释放对象所申请占有的资源

RAII(资源的取得就是初始化,Resource Acquisition Is Initialization)
C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。 – 百度百科

分析下面程序执行结果:

#include <iostream>

using namespace std;

class Test{
public:
    Test(){
        cout << "Test Construct" <<endl;
    }
    ~Test(){
        cout << "Test Deconstruct" << endl;
    }
};

int main(){
    // 局部对象
    cout << "Before {" << endl;
    {
        cout << "After {" << endl; 
        Test t;
        cout << "Before }" << endl;
    }
    cout << "After }" << endl;

    // 动态对象
    cout << "Before {" << endl;
    {
        cout << "After {" << endl; 
        Test* pt = new Test;
        delete pt;
        pt = NULL;
        cout << "Before }" << endl;
    }
    cout << "After }" << endl;
    return 0;
}

问题:new/deletemalloc()free()的区别?

案例:MyString


4.3 引用(别名)
4.3.1 语法

声明:const 类型名& 对象名/类型名& 对象名
使用:与对象变量、基本类型变量一样

(1)基础知识:
int& b = a; //① 引用
cout << “&b:” << &b << endl;//②定义好变量,表示取地址符号
③引用其实就是一个别名,a与b代表的是相同的对象
(2)案例:001_reference.cpp

 #include <iostream>
using namespace std;

int main(){
	int a(10);
	int& b = a;
	cout << "&a:" << &a << "\t" << a << endl;
	cout << "&b:" << &b << "\t" << b << endl;
	 a = 100;
	cout << "&a:" << &a << "\t" << a << endl;
	cout << "&b:" << &b << "\t" << b << endl;
	 b = 1000;
	cout << "&a:" << &a << "\t" << a << endl;
	cout << "&b:" << &b << "\t" << b << endl;


}
4.3.2 何处使用引用
  • 1.避免空指针,取代指针的用法
  • 2.函数参数列表
  • 3.函数返回值
  • 4.成员变量 -- 对象初始化时,必须显示初始化的变量

(1.1)避免空指针,取代指针的用法一:传递参数的三种方式(Swap.cpp):

①值传递(不能修改地址的内容)
#include <iostream>
using namespace std;


void Swap(int m, int n){
	int t = m;
	m = n;
	n = t;

}
int main(){
	int n = 10;
	int m = 100;
	Swap(n,m);
	cout << "n:" << n << "\tm:" << m << endl;
}

②指针传递(可以修改地址的内容,但是解引用比较麻烦)

#include <iostream>
using namespace std;


void Swap(int *m, int *n){
	int t = *m;
	*m = *n;
	*n = t;

}
int main(){
	int n = 10;
	int m = 100;
	Swap(n,m);
	cout << "n:" << n << "\tm:" << m << endl;
}

③引用传递(避免空指针,野指针)

#include <iostream>
using namespace std;


void Swap(int &m, int &n){
	int t = m;
	m = n;
	n = t;

}
int main(){
	int n = 10;
	int m = 100;
	Swap(n,m);
	cout << "n:" << n << "\tm:" << m << endl;
}

(1.2)取代指针的用法二:多个返回值(Divide.cpp)

为何使用引用
  • 避免对象复制
  • 避免传递空指针
  • 使用方便

类型:

  • 基本类型(boolcharintshortfloat)
  • 复合类型(指针、数组、引用)
  • 自定义类型(structunionclass)

引用作用:取代指针

void Func(int* n){
    *n = 2;
}
void Func(int& n){
    n = 3;
    cout << &n << endl;
}
int main(){
    int a =1;
    cout << &a << endl;
    Func(&a);
    cout << a << endl;
    Func(a);
    cout << a << endl;
}
引用与指针的区别
  • 指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。
  • 引用只能在定义时被初始化一次,之后不可变;指针可变;
  • 引用不能为空,指针可以为空;
  • 引用使用时无需解引用*,指针需要解引用;
  • sizeof 引用得到的是所指向的变量/对象的大小,而sizeof 指针得到的是指针本身的大小;

引用通常用于两种情形:成员变量和函数的参数列表以及函数返回值。

4.4 拷贝/复制构造函数
语法:
类名(类名& 形参){ 
    函数体
}

或者

类名(const 类名& 形参){
    函数体
}
调用时机

手动调用

类名 对象名;  // 调用默认构造函数
类名 对象2 = 对象1;    // 调用复制构造函数
类名 对象3(对象1);     // 调用复制构造函数

自动调用

  • 一个对象作为函数参数,以值传递的方式传入函数体
  • 一个对象作为函数返回值,以值从函数返回
  • 一个对象用于给另外一个对象进行初始化(赋值初始化)

实践说明:

  • gcc/clang自动RVO/NRVO优化,不执行拷贝构造函数。可以在编译命令添加选项禁止-fno-elide-constructors;
  • VC在Debug环境下返回值执行拷贝构造函数,在Release环境下实施RVO/NRVO优化。
4.5 默认拷贝构造函数
  • 本质:内存拷贝
  • 作用:复制一个已经存在的对象

问题

  1. 一个类可以多个拷贝构造函数吗?
  2. 拷贝构造函数的参数可以不是引用吗?例如Test(Test t)
  3. 如何禁用默认拷贝构造函数?在类定义,添加私有的复制构造函数的声明,但不实现。

下列程序的执行结果

#include <iostream>
#include <string>

using namespace std;

class Test {
public:
  Test() {}
  Test(Test &t) { cout << "Test(Test &t)" << endl; }
  Test(Test const &t) { cout << "Test(Test const &t)" << endl; }
};

int main() {
  Test t1;
  Test t2 = t1;
  const Test t3;
  Test t4 = t3;
}

5. 赋值运算符重载函数

语法
类名& operater=(const 类名& 形参){
  // 赋值操作
  return *this;
}
调用时机
  • 赋值
默认赋值运算符重载函数
  • 内存拷贝
作用
  • 赋值

如何禁用默认赋值运算符重载函数?在类定义,添加私有的赋值运算符重载函数的声明,但不实现。

拷贝构造函数与赋值操作符的区别

  • 拷贝构造函数:当一个已经存在的对象来初始化一个未曾存在的对象
  • 赋值操作符:当两个对象都已经存在

问题下面那些是拷贝构造函数与赋值操作符?各有几次?

Demo a;
Demo b;
b = a;
Demo c = a;
Demo d = Demo(a);

扩展阅读

  • <<Effective C++ 中文第三版>>
    • 条款10:令operator=返回一个reference to *this
    • 条款11:在operator=中处理“自我赋值”
    • 条款12:复制对象时勿忘其每一个成分

6. 深拷贝(Memberwise Copy)与浅拷贝(Bitwise Copy)

  • 浅拷贝:编译器默认生成的类实例间拷贝行为,对带有指针的类来说会引发 memory leak。
  • 深拷贝:用户定义的行为(实质是一种构造函数)。
问题

一个类中存在指针类型的成员变量。并且类构造和析构管理指针的申请与释放。

class Demo{
public:
    Demo(int n){
        p = new int(n);
    }
    ~Demo(){
        delete p;
        p = NULL;
    }
private:
    int* p;
};

调用拷贝构造函数会出现什么情况?

Demo a(10);
Demo b = a;
解决

赋值运算符重载是否会有相同情况?

区别
  • 浅拷贝:只拷贝指针地址
  • 深拷贝:重现分配堆内存,拷贝指针指向内容
最佳实践

三大定律(Rule of three / the Law of The Big Three / The Big Three)
如果类中明确定义下列其中一个成员函数,那么必须连同其他二个成员函数编写至类内,即下列三个成员函数缺一不可:

  • 析构函数(destructor)
  • 复制构造函数(copy constructor)
  • 复制赋值运算符(copy assignment operator)

扩展阅读

  • <<Effective C++ 中文第三版>>
    • 条款5:了解C++默默编写并调用那些函数
    • 条款6:若不想使用编译器自动生成的函数,就该明确拒绝

7. 友元细说

作用
  • 非成员函数访问类中的私有成员
分类
  • 全局友元函数:将全局函数声明成友元函数
  • 友元成员函数:类的提前引用声明,将一个函数声明为多个类的友元函数
  • 友元类:将整个类声明为友元
特点
  • 友元关系单向性
  • 友元关系不可传递

8. const限定符

  • 本质:不可修改

  • const与变量/对象

    const 类型 变量 = 初始值;
    const 类型 对象;

例如:

const int size = 4;

现在比较前卫写法

类型 const 变量 = 初始值;
类型 const 对象;

例如:

int const size = 4;
  • 定义时必须初始化

  • 全局作用域声明的const变量默认作用域是定义所在文件

  • const对象只能调用const成员函数

  • const与宏定义#define的区别

const

宏定义#define

编译器处理方式

编译运行阶段使用

预处理阶段展开/替换

类型

有具体的类型

没有类型

安全检查

编译阶段会执行类型检查

不做任何类型检查

存储方式

分配内存

不分配内存

  • const与指针

No.

类型

语法

作用

1

const指针

类型* const 变量 = 初始值;

指针指向地址不能改变

2

指向const对象的指针

const 类型* 变量 = 初始值; 类型 const* 变量 = 初始值;

指针指向对象不能改变

3

指向const对象的const指针

const 类型* const 变量 = 初始值;

指针指向地址和对象不能改变

No.2 是使用最频繁的方式。

  • const与引用

类型 const &变量 = 初始值;const 类型& 变量 = 初始值;都是引用对象不能改变。

  • const与函数的参数和返回值

类型

语法

作用

说明

const参数

返回值类型 函数(const 类型 形参)

函数内部不能改变参数的值

这样的参数的输入值

const返回值

const 返回值类型 函数(形参列表)

函数的返回值不能改变

常用于字符串/指针

  • const成员变量

  • 不能在类声明中初始化const数据成员

  • const成员变量只能在类构造函数的初始化列表中初始化

    class 类名{
    public:
    类名(类型 形参):成员变量(形参){}
    private:
    const 类型 成员变量;
    }

如果使用const成员变量不能使用赋值运算符重载函数

  • const成员函数
    成员函数不能修改类中任何成员变量。一般写在成员函数的最后来修饰。

  • 声明

    class 类名{
    public:
    返回值类型 函数名(形参列表)const;
    }

  • 定义

    返回值类型 函数名(形参列表)const;

  • 示例

    #include

    using namespace std;

    class Test{
    public:
    void Print() const{
    cout << “Test” << endl;
    }
    };

    void Func(const Test& t){
    t.Print();
    }
    int main(){
    Test t;
    Func(t);// 变量
    Func(Test());// 匿名对象
    const Test t2;
    Func(t2); // 只读对象
    t2.Print();
    }

必须在成员函数的声明和定义后都加上const

const修饰位置

作用

变量

变量不可修改,通常用来替代#define

对象/实例

对象的成员变量不可修改,只能调用const成员函数

函数参数

参数不能在函数内部修改,只作为入参

函数返回值

返回的结果不能被修改,常用于字符串

成员变量

只能在初始化列表中初始化

成员函数

不改变成员变量

只要能够使用const,尽量使用const

9. static限定符

本质:
  • 生存周期:整个程序的生存周期。
  • 作用域:属于类,不属于对象。
语法
  • 声明

    class 类名{
    static 返回类型 函数(形参列表);
    };

  • 定义

    返回类型 类名::函数(形参列表){
    函数体;
    }

  • 调用

    • 通过类名(Class Name)调用

      类名::函数(实参列表);

    • 通过对象(Object)调用

      对象.函数(实参列表);

规则
  • static只能用于类的声明中,定义不能标示为static
  • 非静态是可以访问静态的方法和函数
  • 静态成员函数可以设置private,public,protected访问权限
禁忌
  • 静态成员函数不能访问非静态函数或者变量
  • 静态成员函数不能使用this关键字
  • 静态成员函数不能使用cv限定符(constvolatile)

因为静态成员函数是属于类而不是某个对象。

volatile是一个不常用的关键字,作用是改善编译器的优化能力。

静态成员变量

语法:

  • 在类定义中声明,但是在类实现中初始化。
  • 在声明时需要指定关键字static,但是实现时不需要指定static
  • 对象的大小不包含静态成员变量

因为静态成员变量是属于类而不是某个对象。静态成员变量所有类的对象/实例共享。

static修饰位置

作用

变量

静态变量

函数

只源文件内部使用的函数

成员变量

对象共享变量

成员函数

类提供的函数,或者作为静态成员对象的接口

单例模式:使用静态成员变量和静态成员函数。

示例:
统计某类实例个数
习题:
下面代码输出为()[美团点评2019秋招]

#include <bits/stdc++.h>
using namespace std;
class Test{
public:
    static char x;
};
char Test:x='a';
int main(){
    Test exp1,exp2;
    cout<<exp1.x<<" ";
    exp1.x+=5;
    cout<<exp2.x <<endl;
}

A. f a
B. a f
C. a a
D. f f


10. const static限定符

#include <iostream>

using std::cout;
using std::endl;

class StaticConstTest{
public:
    void print(){
        cout << test1 << " " << test2 << endl;
    }
private:
    static const int test1 = 1;
    static const int test2;
};

/* static */ const int StaticConstTest::test2 = 2;

int main(){
    StaticConstTest sct;
    sct.print();
}

变量类型

声明位置

一般成员变量

在构造函数初始化列表中初始化

const成员常量

必须在构造函数初始化列表中初始化

static成员变量

必须在类外初始化

static const/const static成员变量

变量声明处或者类外初始化

注意:static const/const static成员变量必须是整形。

11. 内联函数

inline -- 宏定义的接班人

条件

一般用在代码比较简单的函数

语法
  • 关键字inline必须与函数实现/定义体放在一起才能使函数成为内联,将inline放在函数声明前面不起任何作用
  • 定义在类声明之中的成员函数将自动地成为内联函数
慎用内联
  • 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高
  • 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大
  • 不要随便地将构造函数和析构函数的定义体放在类声明中
本质

内联函数的代码直接替换函数调用,省去函数调用的开销

12. 运算符重载

案例:实现复数类

语法

运算符重载主要有两种方式实现:

  • 成员函数运算符重载

    返回值类型 operator 运算符(参数){
    函数体
    }

  • 友元函数运算符重载

    friend 返回值类型 operator 运算符(形参列表) {
    函数体
    }

分类

No.

类型

运算符

成员函数

友元函数

1

双目算术运算符

+ - * / %

类名 operator 运算符(const 类名&) const

类名 operator 运算符(const 类名&, const 类名&)

2

关系运算符

== != > >= < <=

bool operator 运算符 (const 类名& ) const

bool operator 运算符 (const 类名&,const 类名&)

3

双目逻辑运算符

&& ¦¦

bool operator 运算符 (const 类名& ) const

bool operator 运算符 (const 类名&,const 类名&)

4

单目逻辑运算符

!

bool operator !() const

bool operator ! (const 类名&)

5

单目算术运算符

+ -

类名& operator 运算符 ()

类名& operator 运算符 (const 类名&)

6

双目位运算符

& ¦

类名 operator 运算符 (const 类名& ) const

类名 operator 运算符 (const 类名& ,const 类名& )

7

单目位运算符

~

类名 operator ~ ()

类名 operator ~ (类名&)

8

位移运算符

<< >>

类名 operator 运算符 (int i) const

类名 operator 运算符 (const 类名&,int i)

9

前缀自增减运算符

++ --

类名& operator 操作符 ()

类名& operator 操作符 (类名&)

10

后缀自增减运算符

++ --

类名 operator ++ (int)

类名 operator ++ (类名&,int)

11

复合赋值运算符

+= -= *= /= %= &= ¦= ^=

类名& operator 运算符 (const 类名& )

类名& operator += (类名&,const 类名&)

12

内存运算符

new delete

参见说明

参见说明

13

流运算符

>> <<

参见说明

14

类型转换符

数据类型

参见说明

15

其他运算符重载

= [] () ->

参见说明

说明
1.内存运算符
  • 成员函数

    void operator new(size_t size);
    void operator new[](size_t size);
    void operator delete(void
    p);
    void operator delete [](void
    p);

  • 友元函数

    void operator new(类名,size_t size);
    void operator new[](类名&,size_t size);
    void operator delete(类名&,void
    p);
    void operator delete [](类名&,void
    p);

2.流运算符

流运算符只能使用友元函数实现。

inline ostream &operator << (ostream&, 类名&)
inline istream &operator >> (istream&, 类名&)
3.类型转换符

这些运算符只能使用成员函数实现。

operator char* () const;
operator int ();
operator const char () const;
operator short int () const;
operator long long () const;
4.其他运算符重载

这些运算符只能使用成员函数实现。

类名& operator = (const 类名& );
char operator [] (int i);//返回值不能作为左值
const char* operator () ();
T operator -> ();
规则
  • 不能重载的运算符:成员运算符.、作用域运算符::sizeof、条件运算符?:
  • 不允许用户自定义新的运算符,只能对已有的运算符进行重载
  • 重载运算符不允许改变运算符原操作数的个数
  • 重载运算符不能改变运算符的优先级
  • 重载运算符函数不能有默认的参数,会导致参数个数不匹配
本质

函数重载

问题
  1. 为什么cout可以接收各种内置类型“对象”?因为标准库对<<做了针对各种内置类型的运算符重载。

    #include
    #include
    #include

    using namespace std;

    string operator+(int n,string const& str){
    ostringstream oss;
    oss << n << str;
    return oss.str();
    }
    string operator+(string const& str,int n){
    ostringstream oss;
    oss << str << n;
    return oss.str();
    }
    string operator+(float n,string const& str){
    ostringstream oss;
    oss << n << str;
    return oss.str();
    }
    string operator+(string const& str,float n){
    ostringstream oss;
    oss << str << n;
    return oss.str();
    }
    string operator+(double n,string const& str){
    ostringstream oss;
    oss << n << str;
    return oss.str();
    }
    string operator+(string const& str,double n){
    ostringstream oss;
    oss << str << n;
    return oss.str();
    }

    // 注意重载中的类型自动转换
    int main(){
    string a = " test ";
    string res1 = “str” + a;
    cout << res1 << endl;

    auto res2 = 123 + a;
    cout << res2 << endl;
    auto res3 = a + 123;
    cout << res3 << endl;
    
    auto res4 = 123.456f + a;
    cout << res4 << endl;
    auto res5 = a + 123.456f;
    cout << res5 << endl;
    
    auto res4 = 123.456 + a;
    cout << res4 << endl;
    auto res5 = a + 123.456;
    cout << res5 << endl;
    

    }

有如下类模板定义:[美团点评2019秋招]

class A{
    int n;
public:
    A(int i):n(i) {}
    A operator+(A b){
        return A(n + b.n);
    }
};

己知a, b是A的两个对象,则下列表达式中错误的是()
A. 3+a
B. a.operator+(b)
C. a+b
D. a+1

发布了33 篇原创文章 · 获赞 8 · 访问量 2262

猜你喜欢

转载自blog.csdn.net/Pierce110110/article/details/104332908
今日推荐