7. 友元细说
作用
- 非成员函数访问类中的私有成员
分类
- 全局友元函数:将全局函数声明成友元函数
- 友元成员函数:类的提前引用声明,将一个函数声明为多个类的友元函数
- 友元类:将整个类声明为友元
特点
- 友元关系单向性
- 友元关系不可传递
8. const 限定符
8.1 本质:不可修改
8.2 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 是使用最频繁的方式。
8.4 const与引用
类型 const &变量 = 初始值;与 const 类型& 变量 = 初始值;都是引用对象不能改变。
8.5 const 与函数的参数和返回值
类型 | 语法 | 作用 | 说明 |
---|---|---|---|
const参数 | 返回值类型 函数(const 类型 形参) | 函数内部不能改变参数的值 | 这样的参数的输入值 |
const返回值 | const 返回值类型 函数(形参列表) | 函数的返回值不能改变 | 常用于字符串/指针 |
- const成员变量
- 不能在类声明中初始化const数据成员(C++11可以)
- const成员变量只能在类构造函数的初始化列表中初始化
class 类名{
public:
类名(类型 形参):成员变量(形参){}
private:
const 类型 成员变量;
}
- 应用:const成员变量一般用于类定义后不可修改的信息,例如:学生学号。
注意:
- 使用const成员变量不能省略构造函数(引用类型的成员变量相同)
- 使用const成员变量不能使用赋值运算符重载函数
- const成员函数
成员函数不能修改类中任何成员变量。一般写在成员函数的最后来修饰。 - 声明
class 类名{
public:
返回值类型 函数名(形参列表)const;
}
- 定义
返回值类型 函数名(形参列表)const;
- 示例
#include <iostream>
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限定符
9.1 本质:
- 生存周期:整个程序的生存周期,(可以视为全局共享变量,其他对象均可以访问,但是static不一定能访问其他)。
- 作用域:属于类,不属于对象。
static修饰位置 | 作用 |
---|---|
变量 | 静态变量 |
函数 | 只源文件内部使用的函数 |
成员变量 | 对象共享变量 |
成员函数 | 类提供的函数,或者作为静态成员对象的接口 |
单例模式:使用静态成员变量和静态成员函数。
9.2 语法
- 声明
class 类名{
static 返回类型 函数(形参列表);
};
- 定义
返回类型 类名::函数(形参列表){
函数体;
}
- 调用
- 通过类名(Class Name)调用
类名::函数(实参列表);
- 通过对象(Object)调用
对象.函数(实参列表);
9.3 静态成员变量
a. static 关键字只能用在静态成员变量的声明前,不能用在定义初始化,在类定义中声明,但是在类实现中初始化,不带static。静态成员函数也是如此;
static int count; //静态成员变量声明
static void UpdateCount(int n);
/*static*/ int Test::count = 100; //静态成员必须在类外初始化
/*static*/ void Test::UpdateCount(int n){ // 静态成员函数可以访问静态成员变量,但是不能访问非静态的成员变量
count = n;
}
b. 非静态变量属于类,但是不属于对象,因为静态成员变量是属于类而不是某个对象。静态成员变量的大小不算在对象/类型大小里面。
cout << sizeof(Test) << endl;
大小的计算:
(1)32位:string(24)+int(4) //填充为4的整数倍;
(2)64位:string(32)+int(4) + 4 //填充为8的倍数;
和stattic对象无关;
c. 静态成员变量所有类的对象/实例共享。
t1.Get();
t2.Get();
t3.Get();
d. 非静态是可以访问静态的方法和函数,静态成员函数,仅仅能访问静态变量;
/*static*/ void Test::UpdateCount(int n){ // 静态成员函数可以访问静态成员变量,但是不能访问非静态的成员变量
count = n;
}
- 完整案例
#include <iostream>
#include <string>
using namespace std;
class Test{
static int count;/
string name;
int getCount; // 静态成员变量不属于对象,静态成员变量的大小不算在对象/类型大小里面
public:
Test(const string& name):name(name),getCount(0){}i
void Get();
static void UpdateCount(int n);
};
void Test::Get(){
--count;
++getCount;
cout << name << " leave " << count << " have " << getCount << endl;}
/*static*/ int Test::count = 200;
/*static*/ void Test::UpdateCount(int n){
Test::count = n; //静态成员函数,仅仅能访问静态变量;
}
int main(){
cout << sizeof(Test) << endl;
//cout << Test::count << endl;
Test t1("A"),t2("B"),t3("C");
t1.Get(); // 静态成员变量所有类的对象/实例共享。
t2.Get();
t3.Get();
t1.Get();
Test::UpdateCount(100);
t2.Get();
t1.Get();
t3.Get();
}
9.4 静态成员函数
- 静态成员函数不能访问非静态函数或者变量
- 静态成员函数不能使用this关键字,this 表示对象;
- 静态成员函数不能使用cv限定符(const与volatile)
- 静态成员函数可以设置private,public,protected访问权限
- static 关键字只能用在静态成员函数的声明前,不能用在定义初始化,在类定义中声明,但是在类实现中初始化,不带static。
9.5 案例二:原理相同,实现对话
#include <iostream>
using namespace std;
class StaticSimple{
string name;
static int num;
int count = 0;
public:
StaticSimple(const string& name):name(name){}
void Print(const string& s){
cout << ++num <<" "<< name << ":\"" << s << "\"" << "--" << ++count << endl; // 静态成员函数中不允许使用非静态的成员变量
}
static void PrintComment(const string& s){
cout << ++num <<" "<< s << endl;
}
};
// 静态成员必须在类外定义初始化
// static 关键字只能用在静态成员变量的声明前,不能用在定义初始化
/*static*/ int StaticSimple::num = 0;
int main(){
StaticSimple s("John");
s.Print("Hello");
s.PrintComment("Hello World");
StaticSimple::PrintComment("Hello World");
StaticSimple t("Jobs");
t.Print("Apple");// 静态变量在对象之间可以共享数据
s.Print("What?");
t.Print("Big Apple");
s.Print("OK");
}
9.6 面试
示例:
统计某类实例个数
习题:
下面代码输出为()[美团点评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
答案: B
原因:静态变量之间进行共享,共同修改;
10. const static限定符
10.1 总结(实际应用):
a. 数字类型可以在类定义初始化也可以在类外面((INT ,浮点,char));
const static char x = 'a';
b. 非数字类型可以在类定义初始化必须在类外面;
const static string y = "abc";//错误:非数字类型可以在类定义初始化必须在类外面;
const static string y ;
/*static*/ const string Test::y = "abc"; //正确
c. 不能修改const变量;
e1.x += 5; .不能修改const变量;
d. 建议将static注释;
- 完整案例
#include <iostream>
using namespace std;
class Test{
public:
const static char x = 'a'; //数字类型可以在类定义初始化也可以在类外面((INT ,浮点,char));
const static float z = 1.2;
//const static string y = "abc";//错误:非数字类型可以在类定义初始化必须在类外面;
const static string y ; //正确非数字类型可以在类定义初始化必须在类外面;
};
/*static*/ const string Test::y = "abc"; //建议将static注释;
int main(){
Test e1, e2;
cout << e1.x << " " ;
//e1.x += 5; .不能修改const变量;
cout << e2.x << " " ;
}
10.2 基础知识
变量类型 | 声明位置 |
---|---|
一般成员变量 | 在构造函数初始化列表中初始化 |
const成员常量 | 必须在构造函数初始化列表中初始化 |
static成员变量 | 必须在类外初始化 |
static const/const static成员变量 | 变量声明处或者类外初始化 |
注意:static const/const static成员变量必须是数字型(INT ,浮点,char)。
11. 内联函数
inline -- 宏定义的接班人
11.1 条件
一般用在代码比较简单的函数
11.2 语法
- 关键字 inline 必须与函数实现/定义体放在一起才能使函数成为内联,将inline放在函数声明前面不起任何作用
- 定义在类声明之中的成员函数将自动地成为内联函数
11.3 慎用内联
- 如果函数体内的代码比较长,使用内联将导致内存消耗代价较高
- 如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大
- 不要随便地将构造函数和析构函数的定义体放在类声明中
- 代码短,编辑器优先视为内联函数;过长的话,即使加上inline 也不看作内联函数。
11.4 本质
内联函数的代码直接替换函数调用,省去函数调用的开销
11.5 总结(实际应用)
a. inline 修饰词只能放在类外;不能放在类的声明处;
b.类里面实现的函数默认为内联函数;
c.如何应用:
- 将内联函数的声明定义在类里面,内联函数的实现定义在类的外面;
void Get();
inline /*static*/ void Test::UpdateCount(int n){ // 静态成员函数可以访问静态成员变量,但是不能访问非静态的成员变量
count = n;
}
- 成员函数的实现;+ 静态变量的初始化
/*static*/ int Test::count = 50;
- 实验函数
- Test.h
#ifndef _007_TEST_H_
#define _007_TEST_H_
#include <iostream>
using namespace std;
class Test{
string name;
int getCount;
public:
static int count;// 静态成员变量的大小不算在对象/类型大小里面
// 静态成员变量不属于对象属于类。
public:
Test(const string& name):name(name),getCount(0){}
void Get();
static void UpdateCount(int n);
};
inline void Test::Get(){
--count;
++getCount;
cout << name << " leave " << count << " have " << getCount << endl;
}
inline /*static*/ void Test::UpdateCount(int n){ // 静态成员函数可以访问静态成员变量,但是不能访问非静态的成员变量
count = n;
}
#endif // _TEST_H_
- Test.cpp
#include "007_Test.h"
/*static*/ int Test::count = 50;
- Test_main.cpp
#ifndef _007_TEST_H_
#define _007_TEST_H_
#include <iostream>
using namespace std;
class Test{
string name;
int getCount;
public:
static int count;// 静态成员变量的大小不算在对象/类型大小里面
// 静态成员变量不属于对象属于类。
public:
Test(const string& name):name(name),getCount(0){}
void Get();
static void UpdateCount(int n);
};
inline void Test::Get(){
--count;
++getCount;
cout << name << " leave " << count << " have " << getCount << endl;
}
inline /*static*/ void Test::UpdateCount(int n){ // 静态成员函数可以访问静态成员变量,但是不能访问非静态的成员变量
count = n;
}
#endif // _TEST_H_
12. 运算符重载
12.1 语法
运算符重载主要有两种方式实现:
- 成员函数运算符重载
返回值类型 operator 运算符(参数){
函数体
}
- 友元函数运算符重载
friend 返回值类型 operator 运算符(形参列表) {
函数体
}
12.2 分类
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 | |
9 | 前缀自增减运算符 | ++ – | 类名& operator 操作符 () | 类名& operator 操作符 (类名&) |
10 | 后缀自增减运算符 | ++ – | 类名 operator ++ (int) | 类名 operator ++ (类名&,int) |
11 | 复合赋值运算符 | += -= *= /= %= &= ¦= ^= | 类名& operator 运算符 (const 类名& ) | 类名& operator += (类名&,const 类名&) |
12 | 内存运算符 | new delete | 参见说明 | 参见说明 |
13 | 流运算符 | >> << | - | 参见说明 |
14 | 类型转换符 | 数据类型 | 参见说明 | - |
15 | 其他运算符重载 | = [] () -> | 参见说明 | - |
12.3 总结(实际应用:1,2,5, 9,13,15)
友元函数和成员的区别:
a. 友元函数可以实现交换律,而成员函数不可以;
(1*c2).Print();
b.友元函数可以访问私有变量,实现的时候,参数比较多,且函数不是const;
friend Complex operator*(const Complex& a, const Complex& b);
而成员函数可以调用类,所以参数少,且为const;
Complex operator*(const Complex& a)&{};
- 四则运算的运算符重载(*)
#include <iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex():real(0),imag(0){}
Complex(int real):real(real),imag(0){}
Complex(int real, int imag):real(real),imag(imag){}
void Print(){
cout << real << "+" << imag << "i" <<endl;
}
friend Complex operator*(const Complex& a, const Complex& b);
// 友元函数可以访问私有变量,实现的时候,参数比较多,且函数不是const;
};
Complex operator*(const Complex& a, const Complex& b){
return Complex(a.real*b.real-a.imag*b.imag, a.imag*b.real+a.real*b.imag);
}
int main(){
Complex c0(0,1);
Complex c1(0,1);
Complex c2 = c0*c1;
(1*c2).Print(); //友元函数可以实现交换律,而成员函数不可以;
c2.Print();
c0.Print();
c1.Print();
}
- 单目运算符重载(!= == )(一成员一友元)
#include <iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex():real(0),imag(0){}
Complex(int real):real(real),imag(0){}
Complex(int real, int imag):real(real),imag(imag){}
void Print(){
cout << real << "+" << imag << "i" <<endl;
}
friend bool operator==(const Complex& a, const Complex& b);
bool operator!=(const Complex& a)const{
return real != a.real || imag != a.imag;
}
};
bool operator==(const Complex& a, const Complex& b){
return a.real == b.real && a.imag == b.imag;
}
int main(){
Complex c0(0,1);
Complex c1(0,1);
cout << (c0 ==c1) <<endl;
cout << (c0 !=c1) <<endl;
}
- 单目运算符重载(+ -) 都是成员函数;
#include <iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex():real(0),imag(0){}
Complex(int real):real(real),imag(0){}
Complex(int real, int imag):real(real),imag(imag){}
void Print(){
cout << real << "+" << imag << "i" <<endl;
}
Complex operator-()const{
return Complex(-real,-imag);
}
Complex operator+()const{
return *this;
}
};
int main(){
Complex c0(0,1);
Complex c1(0,1);
(-c0).Print();
}
- 单目运算符重载(前++ 和 后++) 都是成员函数;
a.(前++:先自增,再运算):
Complex operator++(){
++real;
return *this;
}
b.后++:先运算,再自增,多了一个参数):
Complex operator++(int){
Complex res = *this;
++real;
return res;
}
c.运算的结果按照++的先后顺序:
(++c0).Print();
c0.Print();
(c0++).Print();
c0.Print();
- 完整案例
#include <iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex():real(0),imag(0){}
Complex(int real):real(real),imag(0){}
Complex(int real, int imag):real(real),imag(imag){}
void Print(){
cout << real << "+" << imag << "i" <<endl;
}
Complex operator++(){
++real;
return *this;
} //
Complex operator++(int){
Complex res = *this;
++real;
return res;
}
};
int main(){
Complex c0(0,1);
Complex c1(0,1);
(++c0).Print();
c0.Print();
(c0++).Print();
c0.Print();
}
- (流运算符);<< | >>
a.作用:一般的调用:仅仅能打印类的成员变量,不能打印类的对象(即里面成员变量),需要调用成员函数;
s.Print();
但是流i运算符重载可以实现类的对象(即成员变量的)打印;符合c++的写法;
cout << "s:" << s << endl;
b.(<<)流运算符重载必须使用友元函数,不能是成员函数,因为第一个参数是流运算符,不是对象;
friend ostream& operator<<(ostream& os, const Simple& s);
输入的调用对象必须是非const,因为要进行修改;
friend istream& operator<<(ostream& os, Simple& s);
c. 返回流运算符因为要连续引用;
return os;
d. 流运算符重载不存在成员函数的形式;
s.operator<<(cout);
s<<out;
#include <iostream>
using namespace std;
class Simple{
private:
int n;
public:
Simple(int n):n(n){}
void Print(){
cout << n << endl;
}
friend ostream& operator<<(ostream& os, const Simple& s); //b1.流运算符重载必须使用友元函数,不能是成员函数,因为第一个参数是流运算符,不是对象;
friend istream& operator<<(ostream& os, Simple& s); //b2.输入的调用对象必须是非const,因为要进行修改;
};
ostream& operator<<(ostream& os, const Simple& s){
os << s.n;
return os; // c.返回流运算符因为要连续引用;
}
istream& operator<<(istream& is, Simple& s){
is << s.n;
return is; // c.返回流运算符因为要连续引用;
}
int main(){
Simple s(100);
//d.流运算符重载不存在成员函数的形式;
//s.operator<<(cout);
//s<<out;
cout << "s:" << s << endl; //a.可以实现类的对象(即成员变量的)打印;符合c++的写法;
operator<< (operator<<(cout,"s:"),s);
//s.Print(); a.仅仅能打印类的成员变量,不能打印类的对象(即里面成员变量),需要调用成员函数;
cin >> s;
cout << s << endl;
}
- (其他[] )
a. [] 的运算符重载,需要返回引用类型,方便进行修改;b .可以是成员函数;
char& operator[](int index){ //a.【】的运算符重载,需要返回引用类型,方便进行修改;b .可以是成员函数;
- 完整案例
#include <iostream>
#include <cstring>
using namespace std;
class String{
private:
char* str;
public:
String (const char* s){
str = new char[strlen(s)+1];
strcpy(str, s);
}
int size()const{
return strlen(str);
}
char& operator[](int index){ //a.【】的运算符重载,需要返回引用类型,方便进行修改;b .可以是成员函数;
return str[index];
}
friend ostream& operator<<(ostream& os, const String& s);
};
ostream& operator<<(ostream& os, const String&s){
os << s.str;
return os;
}
int main(){
string s("Hello");
for(int i =0; i < s.size();++i){
s[i] = toupper(s[i]);
}
cout << s <<endl;
for(int i =0; i < s.size();++i){
cout << s[i] << endl;
}
string s2("world");
for(int i =0; i < s2.size();++i){
s2[i] = toupper(s2[i]);
}
cout << s2 <<endl;
for(int i =0; i < s2.size();++i){
cout <<(s2[i]) << endl;
}
}
- (其他() )
a. 主要用来处理回调函数,函数指针的问题;
Simple s(10); //a.主要用来处理回调函数,函数指针的问题;
b. 仿函数:不是函数的调用,而是对象对于运算符函数的重载;
s(12); //b。仿函数:不是函数的调用,而是对象对于运算符函数的重载;
- 完整案例
#include <iostream>
using namespace std;
class Simple{
private:
int n;
public:
Simple(int n):n(n){}
void operator()(int a){
cout << (n + a) << endl;
}
};
int main(){
Simple s(10); //a.主要用来处理回调函数,函数指针的问题
s(12); //b。仿函数:不是函数的调用,而是对象对于运算符函数的重载;
s.operator()(12);
}
12.4 说明
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.类型转换符
这些运算符只能使用成员函数实现
operator char* () const;
operator int ();
operator const char () const;
operator short int () const;
operator long long () const;
3.其他运算符重载
这些运算符只能使用成员函数实现
类名& operator = (const 类名& );
char operator [] (int i);//返回值不能作为左值
const char* operator () ();
T operator -> ();
4.规则
-
不能重载的运算符:成员运算符==.==、作用域运算符::、sizeof、条件运算符?:
-
不允许用户自定义新的运算符,只能对已有的运算符进行重载
-
重载运算符不允许改变运算符原操作数的个数
-
重载运算符不能改变运算符的优先级
-
重载运算符函数不能有默认的参数,会导致参数个数不匹配;但是其他函数,对象的调用可以有默人的参数;
Simple t; //a..类对象有默认的参数,通常数据类型为0; 指针为NULL;
解决的方案:
解决办法1:默认构造函数
Simple():n(0){}
Simple():str(NULL){}
解决办法2: 构造函数给定默认参数;
Simple(int n = 0):n(n){} //b2.解决办法2: 构造函数给定默认参数;
String(char* s = NULL):s(s){}
- 完整案例
#include <iostream>
using namespace std;
class Simple{
private:
int n;
public:
//Simple():n(0){} // b1.解决办法1:默认构造函数
Simple(int n = 0):n(n){} //b2.解决办法2: 构造函数给定默认参数;
void operator()(int a){
cout << (n + a) << endl;
}
friend ostream& operator<< (ostream& os, const Simple& s){
os << s.n;
return os;
}
};
int main(){
Simple t; //a..类对象有默认的参数,通常数据类型为0; 指针为NULL;
cout << t << endl;
Simple s(10);
s(12);
s.operator()(12);
}
5.本质
函数重载
-
为什么cout可以接收各种内置类型“对象”?
因为标准库对<<做了针对各种内置类型的运算符重载。
#include <iostream>
#include <algorithm>
#include <sstream>
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;
}
6.实际问题(面试)
- 有如下类模板定义:[美团点评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
正确答案:A。运算符重载;(只有友元函数才能使用非对象的调用);
- 运算符重载的实际应用(Complex类):
(1)<<的重载(省略Print()函数):
ostream& operator<<(ostream& os, const Simple& s){
os << s.real <<"+"<<s.imag<<" i";
return os; // c.返回流运算符因为要连续引用;
}
int main(){
cout << (Complex c(12)) << endl;
}
(2)>>的重载(省略Print()函数):
istream& operator<<(istream& is, Complex& c){
is >> c.real >> c.imag;
return is; // c.返回流运算符因为要连续引用;
}
int main(){
Complex c;
cin >> c;
}
- 调用std 标准库的用法:
写法:从左向右,一层一层的晚会后面写
int n,m;
cin >> n >>m;
operator>>(operator>>(cin,n),m); //原理相同
cout << n <<" "<<m << endl;
operator<<(operator<<(operator<<(operator<<(count,n)," "),m),endl); //原理相同
13. 额外补充(单参构造函数)
a.如果只有一个参数的话,则会默认转化,比那及其会把数字转化为类的对象;
a+1; a.如果只有一个参数的话,则会默认转化,比那及其会把数字转化为类的对象;
b. 如果单参构造函数加上关键字explict时,可以禁止默认转化(常用);
explicit A(int i):n(i)
- 完整案例
#include <iostream>
using namespace std;
class A{
int n;
public:
//b.如果单参构造函数加上关键字explict时,可以禁止默认转化(常用);
explicit A(int i):n(i){ /
cout << "A(" << i << ")" << endl;
}
A operator+(A b){
return A(n+b.n);
}
};
int main(){
A a(10);
A b(20);
// a+1; a.如果只有一个参数的话,则会默认转化,比那及其会把数字转化为类的对象;
// A(1);
}
14. 实战演练,类的实际应用
14.1 this表示地址,*this表示对象
Integer operator++(){
++n;
return *this; //(1)this表示地址,*this表示对象
}
14.2 Integer表明调用的模板为int;运算符重载,调用拷贝构造;对象参数匹配的话,调用定义的、默认的构造函数;
Integer<int> n2(10); //(2)Integer<int>表明调用的模板为int;
//(3)运算符重载,调用拷贝构造;对象参数匹配的话,调用定义的、默认的构造函数;
- 完整案例
#include <iostream>
using namespace std;
template<typename T>
class Integer{
T n;
public:
Integer(T n):n(n){}
Integer operator+(const Integer& num)const{
// n+=num.n;
// return *this;
return Integer(n+num.n);
}
Integer operator++(){
++n;
return *this; //(1)this表示地址,*this表示对象
}
Integer operator++(int){
// Integer tmp(*this); // 调用编译器提供的默认拷贝构造函数Integer(const Integer&)
Integer tmp(n);// 调用构造函数Integer(T n)
++n;
return tmp;
}
friend ostream& operator<< (ostream& os,const Integer& num){
return os << num.n;
}
};
/*
class Integer{
int n;
public:
Integer(const int& n):n(n){}
Integer operator+(const Integer& num)const{
}
};
*/
int main(){
int n = 10;
cout << (n+1) << endl;
cout << n << endl;
cout << ++n << endl;
cout << n << endl;
cout << n++ << endl;
cout << n << endl;
Integer<int> n2(10); //(2)Integer<int>表明调用的模板为int;
//(3)运算符重载,调用拷贝构造;对象参数匹配的话,调用定义的、默认的构造函数;
cout << (n2+1) << endl;
cout << n2 << endl;
cout << ++n2 << endl;
cout << n2 << endl;
cout << n2++ << endl;
cout << n2 << endl;
}