C++基础2(3):类与对象

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.类型语法作用
1const指针类型* 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成员变量一般用于类定义后不可修改的信息,例如:学生学号。

注意:

  1. 使用const成员变量不能省略构造函数(引用类型的成员变量相同)
  2. 使用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 返回类型 函数(形参列表);
};
  • 定义
返回类型 类名::函数(形参列表){
    函数体;
}
  • 调用
  1. 通过类名(Class Name)调用
类名::函数(实参列表);
  1. 通过对象(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.如何应用:

  1. 将内联函数的声明定义在类里面,内联函数的实现定义在类的外面;
void Get();
inline /*static*/ void Test::UpdateCount(int n){ // 静态成员函数可以访问静态成员变量,但是不能访问非静态的成员变量
     count = n;
}
  1. 成员函数的实现;+ 静态变量的初始化
/*static*/ int Test::count = 50;
  1. 实验函数
  • 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 类名& ) constbool operator 运算符 (const 类名&,const 类名&)
3双目逻辑运算符&& ¦¦bool operator 运算符 (const 类名& ) constbool operator 运算符 (const 类名&,const 类名&)
4单目逻辑运算符!bool operator !() constbool 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)&{};
  1. 四则运算的运算符重载(*)
#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();
}
  1. 单目运算符重载(!= == )(一成员一友元)
#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;
}
  1. 单目运算符重载(+ -) 都是成员函数;
#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();
}    
  1. 单目运算符重载(前++ 和 后++) 都是成员函数;

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();
}
  1. (流运算符);<< | >>
    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;

}
  1. (其他[] )
    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;
	}
}
  1. (其他() )

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.本质
函数重载

  1. 为什么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.实际问题(面试)

  1. 有如下类模板定义:[美团点评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。运算符重载;(只有友元函数才能使用非对象的调用);

  1. 运算符重载的实际应用(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;
}
  1. 调用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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值