c++类与对象

C++类与对象

类的封装性

类将具有共性的数据和方法封装在一起,加以权限区分,用户只能通过公共方法 访问私有数据。

类的权限分为:private(私有)protected(保护)public(公有) 3种权限。

在类的外部,只有public修饰的成员才能被访问,在没有涉及继承与派生时, private和protected是同等级的,外部不允许访问。用户在类的外部可以通过public的方法间接访问private和protected数据

类的关键字:class

#include<iostream>
using namespace std;
class data    //类Data 是一个类型
{
private:       //类中 默认为私有,private可以去掉
    int a;     //不要给类中成员 初始化
protected:      //保护
    int b;
public:          //公共
    int c;
    void Showdata(void)    //类中的成员函数 需要对象调用
    {
        cout<<a<<" "<<b<<" "<<c<<endl;   //在类的内部 不存在 权限之分
    }
};
int main()
{
    data test;    //类实例化一个对象
    
    test.c=40;    //类外不能直接访问 类的私有和保护数据,公共可以,test.a//err
    cout<<test.c<<endl;
    return 0;
}

类是一个抽象类型,定义成员时不需要初始化,也没有开辟空间,实例化后才分配存储地址和空间

对于类中的private和protected数据,可通过public中的方法实现对其的访问。

设计一个立方体类

设计立方体类(Cube),求出立方体的面积( 2ab + 2ac + 2bc )和体积( a * b * c),分别用全局函数和成员,函数判断两个立方体是否相等。

#include<iostream>
using namespace std;
class cube
{
private:
    float ml;    //长宽高是私有成员
    float mw;
    float mh;
public:
    float getL(void){return ml;}  //利用公共方法获得长宽高
    float getW(void){return mw;}
    float getH(void){return mh;}
    InitCube(float l,float w,float h)   //利用公共方法设置长宽高
    {
        ml=l;mw=w;mh=h;
    }
    void getS(void)                    //利用公共方法取得S
    {
        cout<<"S="<<(ml*mw+ml*mh+mw*ml)*2<<endl;
    }
    void  getV(void)                    //利用公共方法取得V
    {
        cout<<"V="<<ml*mw*mh<<endl;
    }
    void CubeCom2(cube num2)          //利用公共方法比较两个立方体,num1调用方法时lhw会保存num1的值
    {   if(getH()==num2.getH() && getL()==num2.getL() && getW()==num2.getW())
        {cout<<"Y"<<endl;}
        else {cout<<"N"<<endl;}
    }
};

void CubeCom(cube num1,cube num2)  //利用全局函数比较两个立方体
{   if (num1.getH()==num2.getH() && num1.getL()==num2.getL() && num1.getW()==num2.getW())
    {cout<<"Y"<<endl;}
    else {cout<<"N"<<endl;}
}

int main()
{   cube num1,num2;
    num1.InitCube(10,10,10);
    num2.InitCube(10,10,10);
    num1.getS();
    num2.getV();
    CubeCom(num1,num2);
    num1.CubeCom2(num2);
}

利用成员函数时对象可以比全局函数少一个,因为成员函数调用本身就需要一个函数,

成员函数在类外实现

class Data
{
private:
	int mA;
public:
	//类中声明
	void setA(int a);
	int getA(void);
};
//类外实现
void Data::setA(int a)   //要使用::说明函数的作用域
{
	mA = a;
}
int Data::getA()
{
	return mA;
}

类在其他源文件中实现

类定义在同文件data.h中,而data.cpp是用来实现类的成员函数

data.h头文件

#ifndef DATA_H
#define DATA_H
class Data       //类定义
{
private:
int mA;        //类成员
public:           
void setA(int a);     //类成员函数,声明
int getA(void);
};
#endif

data.cpp

#include "data.h"
void Data::setA(int a)    //  类成员函数,定义
{
mA=a;
}
int Data::getA()
{
return mA;
}

main.cpp

#include <iostream>
#include "data.h"
using namespace std;
int main(int argc, char *argv[])
{
Data ob;    //类的实例化
ob.setA(100);   //成员函数调用
cout<<"mA = "<<ob.getA()<<endl;//mA = 100
return 0;
}

构造函数

类实例化对象的时候 系统自动调用构造函数 完成对象的初始化
如果用户不提供构造函数 编译器 会自动添加一个默认的构造函数(空函数)

构造函数名 和 类名相同,没有返回值类型(连void都不可以)可以有参数(可以重载),权限为public
给对象开辟空间(实例化) 然后调用构造函数(初始化)。

构造函数语法:类名(){}

三种调用方式

  • 括号法
  • 显示法
  • 隐式转换法
class Data
{
public:
int mA;
public:
//无参构造函数
Data()
{
mA=0;
cout<<"无参构造函数"<<endl;
}
//有参构造函数
Data(int a)
{
mA=a;
cout<<"有参构造函数 mA="<<mA<<endl;
}
};
int main()
{
	
Data ob1;     //隐式调用无参构造函数(推荐)      

Data ob2 = Data(); //显示调用无参构造函数
	
Data ob3(10);      //隐式调用有参构造函数(推荐)
	
Data ob4 = Data(10);   //显示调用有参构造函数

Data();            //匿名对象(对象没有名字)(无参) 当前语句结束 立即释放
Data(20);          匿名对象(对象没有名字)(有参) 当前语句结束 立即释放 mA=20
		
Data ob5 = 100;    构造函数隐式转换(类中只有一个数据成员),实际是Data ob5(100)
}

如果用户不提供任何构造函数 编译器默认提供一个空的无参构造。
如果用户定义了构造函数(不管是有参、无参),编译器不再提供默认构造函数。

析构函数

函数名和类名称相同,在函数名前加~,没有返回值类型,没有函数形参。(不能被重载
当对象生命周期结束的时候 系统自动调用析构函数
先调用析构函数执行清理工作 再释放对象的空间。

析构函数语法: ~类名(){}

#include<iostream>
using namespace std;
class Data1
{
public:
int mA;
public:
Data1(int a)
{
mA=a;
cout<<"有参构造函数 mA="<<mA<<endl;
}
~Data1 ()
{
cout<<"析构函数 mA="<<mA<<endl;
}
};
Data1 a1(1);
int main()
{
    Data1 a2(2);
    {
        Data1 a3(3);
    }
    Data1 a4(4);
}

对结果分析构造函数,析构函数的先后,构造是从上到下,析构是从下往上,注意复合语句

有参构造函数 mA=1     //1   Data1 a1(1)构造
有参构造函数 mA=2		//2   Data1 a2(2)构造
有参构造函数 mA=3		//3   Data3 a3(3)构造,他是一个复合语句,语句结束就会被析构掉    
析构函数 mA=3         //4   Data3 a3(3)析构,他是一个复合语句,语句结束就会被析构掉    
有参构造函数 mA=4     //5   Data4 a4(4)构造
析构函数 mA=4        //6   Data4 a4(4)析构
析构函数 mA=2		//7   Data2 a2(2)析构
析构函数 mA=1		//8   Data1 a1(1)析构

一般情况下,系统默认的析构函数就足够。但是如果一个类有指针成员,这个类必须 写析构函数,释放指针成员所指向空间。

#include<string.h>
class Data2
{
public:
char *name;           //指针成员
public:
Data2(){
name=NULL;
}
Data2(char *str)
{
name = new char[strlen(str)+1];     //构造函数用new在堆区开辟了一个空间
strcpy(name, str);
cout<<"有参构造"<<endl;
}
~Data2()
{
if(name != NULL)                  //析构函数判断指针成员是否为空,否则delete[]name释放空间
delete [] name;
cout<<"析构函数"<<endl;
}
};
int main(int argc, char *argv[])
{
Data2 ob("hello world");
cout<<ob.name<<endl;
return 0;
}

拷贝构造函数

拷贝构造:本质是构造函数
拷贝构造的调用时机:旧对象 初始化 新对象 才会调用拷贝构造

#include <iostream>
using namespace std;
class Data
{
public:
int mA;
public:
Data()
{cout<<"无参构造"<<endl;}
Data(int a)
{mA = a;cout<<"有参构造 mA="<<mA<<endl;}
#if 1
Data(const Data &ob)    //拷贝构造的定义形式:ob就是旧对象的引用,必须有const的修饰
{

mA = ob.mA;            //一旦实现了 拷贝构造函数 必须完成赋值操作
cout<<"拷贝构造函数"<<endl;
}
#endif
~Data()
{cout<<"析构函数mA="<<mA<<endl;}
};
int main(int argc, char *argv[])
{
Data ob1(10);//此时ob1是新对象
Data ob2 = ob1;//旧对象给新对象初始化 就会调用拷贝构造函数,此时ob1就是旧对象
cout<<"ob2.mA ="<<ob2.mA<<endl;
return 0;
}

如果用户定义了 拷贝构造或者有参构造 都会屏蔽无参构造
如果用户定义了 无参构造或者有参构造 不会屏蔽拷贝构造

拷贝构造几种调用形式

旧对象给新对象初始化 调用拷贝构造

Data ob1(10);
Data ob2 = ob1;//调用拷贝构造

给对象取别名 不会调用拷贝构造

Data ob1(10);
Data &ob2 = ob1;//不会调用拷贝构造

普通对象作为函数参数 调用函数时 会发生拷贝构

void func(Data ob)//Data ob=ob1
{
}
int main()
{
Data ob1(100);//有参构造
func(ob1);//拷贝构造
}

函数返回值普通对象

Visual Studio会发生拷贝构造

Qtcreater,linux不会发生

拷贝构造的浅拷贝和深拷贝

默认的拷贝构造 都是浅拷贝。浅拷贝不会开辟新的空间,delete释放的是同一个空间。深拷贝开辟新的空间,delete释放每一个空间。
如果类中没有指针成员, 不用实现拷贝构造和析构函数。
如果类中有指针成员,且指向堆区空间, 必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作

#include<iostream>
#include<string.h>
using namespace std;
class Data5
{
public:
    char* name;   //指针成员
public:
Data5()           //无参构造
{name = NULL;}
Data5(char* str)    //有参构造
{
name = new char[strlen(str) + 1];
strcpy(name, str);
cout << "有参构造 name=" << name << endl;
}
Data5(const Data5& ob)//拷贝构造,完成深拷贝
{
name = new char[strlen(ob.name) + 1];//为对象的指针成员申请独立的空间,+1给字符串结束标志
strcpy(name, ob.name);
cout << "拷贝构造函数" << endl;
}
~Data5()
{
cout << "析构函数name = " << name << endl;
if (name != NULL)
{
delete [] name;
name = NULL;
}
}
};
int main()
{
Data5 ob1((char *)"hello world");
Data5 ob2 = ob1;      //触发拷贝构造
}

初始化列表

在类中定义的数据成员一般都是基本的数据类型。但是类中的成员也可以是对象,叫做对象成员
先调用对象成员的构造函数,再调用本身的构造函数。 析构函数和构造函数调用顺序相反,先构造,后析构。

类会自动调用对象成员的无参构造。

类想调用对象成员 有参构造 必须使用初始化列表

#include<iostream>
#include<string.h>
using namespace std;
class A
{
public:
int mA;
public:
A(){mA = 0;cout<<"A的无参构造"<<endl;}
A(int a){mA = a;cout<<"A的有参构造"<<endl;}
~A(){cout<<"A的析构函数"<<endl;}
};

class B
{
public:
int mB;
A ob;//成员对象
public:
B(){cout<<"B类的无参构造"<<endl;}
//初始化列表 成员对象 必须使用对象名+() :ob(a)
B(int a, int b):ob(a)
{mB = b;cout<<"B类的有参构造"<<endl;}
~B(){cout<<"B的析构函数"<<endl;}
};
int main(int argc, char *argv[])
{B ob1(10,20);
cout<<"mA ="<<ob1.ob.mA<<", mB ="<<ob1.mB<<endl;
return 0;}
//初始化列表 成员对象 必须使用对象名+() :ob(a),使用初始化列表调用A的有参构造函数
//B ob1(10,20);先执行ob(a),调用A的有参构造函数mA=10,再调用b的有参构造函数mB=20
B(int a, int b):ob(a)
{
    mB = b;
    cout<<"B类的有参构造"<<endl;
}

B(int a, int b)   //  B ob1(10,20),单纯的给ob.mA赋值,先调用的是A的无参构造,再调用b的有参构造
{
    ob.mA=a;
    mB = b;
    cout<<"B类的有参构造"<<endl;
}

explicit关键字

explicit用于修饰构造函数,防止隐式转化。 是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造) 而言。

class MyString{
public:
explicit MyString(int n){
cout << "MyString(int n)!" << endl;
}
MyString(const char* str){
cout << "MyString(const char* str)" << endl;
}
};
int main(){

//MyString str1 = 1相当于MyString str1(1),有一个隐式转化,导致语义不明给字符串赋值?还是初始化?
//explicit关键字就是防止隐式转化
MyString str2(10);//调用有参构造
MyString str3 = "abcd";//寓意非常明确,给字符串赋值
MyString str4("abcd");
return 0;
}

类的对象数组

对象数组:本质是数组 数组的每个元素是对象。

#include<iostream>
#include<string.h>
using namespace std;
class A
{
public:
int mA;
public:
A()
{
mA = 0;
cout<<"A的无参构造 mA="<<mA<<endl;
}
A(int a)
{
mA = a;
cout<<"A的有参构造mA="<<mA<<endl;
}
~A()
{
cout<<"A的析构函数 mA = "<<mA<<endl;
}
};
int main()
{
//对象数组 每个元素都会自动调用构造和析构函数
//对象数组不初始化 每个元素 调用无参构造
A arr1[5];
//对象数组的初始化 必须显示使用有参构造 逐个元素初始化
A arr2[5]={A(10),A(20),A(30),A(40),A(50) };
int n =sizeof(arr2)/sizeof(arr2[0]);
int i=0;
for(i=0;i<n;i++)
{
cout<<arr2[i].mA<<" ";
}
cout<<endl;
}

动态对象

new创建动态对象

C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为new的运算符里。当用new创建一个对象时,它就在堆里为对象分配内存并调用构造函数完成初始化。New操作符能确定在调用构造函数初始化之前内存分配是成功的。它带有内置的长度计算、类型转换和安全检查

Person* person = new Person;//在堆区创建一个Person对象

delete释放动态对象

new表达式的反面是delete表达式。delete表达式先调用析构函数,然后释放内存

#include<iostream>
#include<string.h>
using namespace std;
class Person{
public:
Person(){
cout << "无参构造函数!" << endl;
pName = new char[strlen("undefined") + 1];
strcpy(pName, "undefined");
mAge = 0;
}
Person(char* name, int age){
cout << "有参构造函数!" << endl;
pName = new char[strlen(name) + 1];
strcpy(pName, name);
mAge = age;
}
void ShowPerson(){
cout << "Name:" << pName << " Age:" << mAge << endl;
}
~Person(){
cout << "析构函数!" << endl;
if (pName != NULL){
delete [] pName;
pName = NULL;
}
}
public:
char* pName;  
int mAge;
};
int main(){
    Person* person1 = new Person;
    Person* person2 = new Person("John",33);
    person1->ShowPerson();
    person2->ShowPerson();
    delete person1;
    delete person2;
}

动态对象数组

当创建一个对象数组的时候,必须对数组中的每一个对象调用构造函数,除了在栈上可以聚合初始化,必须提供一个默认的构造函数。

#include<iostream>
#include<string.h>
using namespace std;
class Person{
public:
Person(){
cout << "无参构造函数!" << endl;
pName = new char[strlen("undefined") + 1];
strcpy(pName, "undefined");
mAge = 0;
}
Person(char* name, int age){
cout << "有参构造函数!" << endl;
pName = new char[strlen(name) + 1];
strcpy(pName, name);
mAge = age;
}
void ShowPerson(){
cout << "Name:" << pName << " Age:" << mAge << endl;
}
~Person(){
cout << "析构函数!" << endl;
if (pName != NULL){
delete [] pName;
pName = NULL;
}
}
public:
char* pName;
int mAge;
};
int main(){
    //栈聚合初始化
    Person person[] = { Person("john", 20), Person("Smith", 22) };
    cout << person[1].pName << endl;
    //创建堆上对象数组必须提供构造函数
    Person* workers = new Person[3];
    delete [] workers;
}

静态成员变量

static修饰的静态成员 属于类而不是对象。(所有对象 共享 一份 静态成员数据)。

static修饰的成员 定义类的时候 必须分配空间。
static修饰的静态成员数据 必须类中定义 类外初始化。

类中定义
static 数据类型  名字
类外初始化
数据类型  作用域::名字=内容
class Data
{
public:
int a;//普通成员数据
//类中定义
static int b;//静态成员数据
};
//类外初始化
int Data::b=100;//不用加static
int main()
{
//静态成员数据 通过类名称直接访问(属于类)
cout<<Data::b<<endl;
//静态成员数据 通过对象访问(共享)
Data ob1;
cout<<ob1.b<<endl;//100
ob1.b = 200;
Data ob2;
ob2.b = 300;
cout<<Data::b<<endl;//300
}

静态成员函数

静态成员函数 是属于类 而不是对象(所有对象 共享)

class Data
{
	static void func()//静态成员函数
	{
	}
}

静态成员函数 可以直接通过类名称访问

静态成员函数内 只能操作静态成员数据。

#include<iostream>
#include<string.h>
using namespace std;
class data{
private:
    static int a;
        int b;
public:
        static int fun(void)
        {   //b=100;err静态成员函数,只能操作静态成员数据,
            return a;}
};
int data::a=100;
int main()
{
    cout<<data::fun()<<endl;//直接通过类名称访问
}

单例模式设计

系统中某个类的对象只能存在一个

#include<iostream>
#include<string.h>
using namespace std;
class data{
private:
    data(){}
    data(const data &ob){}
    ~data(){}
    static data* const p;   //静态成员变量定义,p指向一个data对象,const修饰唯一
public:
    static data * getdata(void)//静态成员函数返回对象地址
    {return p;}
    void test(void)
    {cout<<"ok"<<endl;}
};
data* const data::p=new data;//静态成员变量初始化
int main()
{
    data *p1=data::getdata();//直接通过类名称访问静态成员函数
    p1->test();//调用成员函数
}

成员变量和函数的存储

c++实现了“封装”,“数据”和“处理数据的操作(函数)”是分开存储的。 c++中的非静态数据成员直接内含在类对象中成员函数虽然内含在class声明之内,却不出现在对象中每一个非内联成员函数只会诞生一份函数实例

C++类对象中的变量和函数是分开存储。

非静态成员变量占对象空间

静态成员变量不占对象空间

函数也不占对象空间,所有函数共享一个函数实例

静态成员函数也不占对象空间

class Person {
public:
	Person() {
		mA = 0;
	}
	//非静态成员变量占对象空间
	int mA;
	//静态成员变量不占对象空间
	static int mB; 
	//函数也不占对象空间,所有函数共享一个函数实例
	void func() {
		cout << "mA:" << this->mA << endl;
	}
	//静态成员函数也不占对象空间
	static void sfunc() {
	}
};

int main() {

	cout << sizeof(Person) << endl;

	system("pause");

	return 0;
}

this指针

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

c++通过提供特殊的对象指针,this指针,解决这一块代码是如何区分那个对象调用自己的问题

this指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this,完成链式操作
#include<iostream>
#include<string.h>
using namespace std;
class data{
public:
    int a;
    void fun(int a)
    {this->a=a;}//函数形参和成员同名可以使用this指针解决。
    data& fun2(char *str)
     {cout<<str;return *this;}
};
int main()
{
    data ob;
    ob.a=1;
    cout<<ob.a<<endl;
    ob.fun(10);
    cout<<ob.a<<endl;
    ob.fun2("nihao").fun2("woshi").fun2("ikun");//链式操作
}

const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数
#include<iostream>
#include<string.h>
using namespace std;
class Data
{
public:int a;int b;
    mutable int c;//加关键字mutable后,在常函数中依然可以修改
public:
Data(int a, int b,int c)
{
this->a = a;
this->b = b;
this->c = c;
}
//const 修饰成员函数为只读(该成员函数不允许对 成员数据 赋值) mutable修饰的成员除外
void showData(void) const
{
//a = 100;//err
c=100;
cout<<a<<" "<<b<<" "<<c<<endl;
}
    void fun()
{cout<<"ok"<<endl;}
};
int main()
{
Data ob1(10,20,30);
ob1.showData();
const Data ob2(1,2,3);
ob2.c=1;//常对象可以修改mutable修饰成员变量
//ob2.a=3;err,
//ob2.b=4;err,常对象不能修改成员变量的值,但是可以访问
//ob2.fun();err,常对象只能调用常函数
cout<<ob2.a<<ob2.b<<ob2.c<<endl;
}

友元

使用friend关键字声明友元
friend关键字只出现在声明处,一个函数或者类 作为了另一个类的友元 那么这个函数或类 就可以直接访问 另一个类的私有数据。
友元 重要用在运算符重载上。

普通全局函数作为类的友元

#include<iostream>
#include<string.h>
using namespace std;
class room{
    friend void vt(room &room);//声明此函数为类的友元
private:
    string bedroom;
public:
    string setingroom;
public:
    room(string bedroom,string setingroom)
    {
        this->bedroom=bedroom;
        this->setingroom=setingroom;
    }
};
void vt(room &room)//bedroom is private必须声明友元才能访问
{
    cout<<room.setingroom<<endl;
    cout<<room.bedroom<<endl;
}
int main()
{
    room room("chuang","shafa");
    vt(room);
}

类的某个成员函数 作为另一个类的友元

#include<iostream>
#include<string.h>
using namespace std;
class room;  //向前声明 只能说明类名称,
class boy{
public:
    void vt01(room &room);  //声明成员函数
    void vt02(room &room);  //声明成员函数
};
class room{
    friend void boy::vt01(room &room);//声明作用域在boy的成员函数为类的友元
    friend void vt(room &room);//声明此函数为类的友元
private:
    string bedroom;
public:
    string setingroom;
public:
    room(string bedroom,string setingroom)
    {
        this->bedroom=bedroom;
        this->setingroom=setingroom;
    }
};
void vt(room &room)//bedroom is private必须声明友元才能访问
{
    cout<<room.setingroom<<endl;
    cout<<room.bedroom<<endl;
}
void boy::vt01(room &room)   //定义成员函数,一定要说明作用域
{
   cout<<room.bedroom<<endl;
}
void boy::vt02(room &room)
{
    cout<<room.setingroom<<endl;
}
int main()
{
    room room("chuang","shafa");
    vt(room);
    boy wang;
    wang.vt01(room);
    wang.vt02(room);
}

整个类作为 另一个类的友元

#include<iostream>
#include<string.h>
using namespace std;
class room;  //向前声明 只能说明类名称,
class boy{
public:
    void vt01(room &room);  //声明成员函数
    void vt02(room &room);  //声明成员函数
};
class room{
    friend class boy;//boy为类的友元
    friend void vt(room &room);//声明此函数为类的友元
private:
    string bedroom;
public:
    string setingroom;
public:
    room(string bedroom,string setingroom)
    {
        this->bedroom=bedroom;
        this->setingroom=setingroom;
    }
};
void vt(room &room)//bedroom is private必须声明友元才能访问
{
    cout<<room.setingroom<<endl;
    cout<<room.bedroom<<endl;
}
void boy::vt01(room &room)   //定义成员函数,一定要说明作用域
{
   cout<<room.bedroom<<endl;
}
void boy::vt02(room &room)
{
    cout<<room.setingroom<<endl;
}
int main()
{
    room room("chuang","shafa");
    vt(room);
    boy wang;
    wang.vt01(room);
    wang.vt02(room);
}

友元的注意事项

1.友元关系不能被继承
2.友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。
3.友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友

运算符重载

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。

弄懂运算符的运算对象的个数。(个数决定了 重载函数的参数个数)

识别运算符左边的运算对象 是类的对象 还是其他.
类的对象:全局函数实现 或者 成员函数实现(少一个参数)
其他:只能是全局函数实现

#include<iostream>
#include<string>
using namespace std;
class stu{
   friend ostream& operator <<(ostream &out,stu lulu);//成为类的友元
   friend stu operator +(stu &lulu,stu &caicai);//成为类的友元
   friend istream& operator >>(istream &cin ,stu &kunkun);//成为类的友元
private:
    string name;
    int num;
    float score;
public:
    stu(){}
    stu(string name,int num,float score):name(name),num(num),score(score){}
    bool operator ==(stu &caicai)  //利用成员函数重载==运算符,可以少一个参数
    {
        if(name==caicai.name && num==caicai.num && score==caicai.score)
        {return true;}
        else {return false;}
    }
};


ostream& operator <<(ostream &cout,stu lulu) //<<重载不要取引用,调用类的private,需要成为类的友元
{
    cout<<lulu.name<<" "<<lulu.num<<" "<<lulu.score<<endl;
    return cout; //cout类型为ostream,返回他,可以进行链式操作,返回的是一个引用
}


istream& operator >>(istream &cin ,stu &kunkun){   //全局函数重载>>
    cin>>kunkun.name>>kunkun.num>>kunkun.score;
    return cin;
}


stu operator +(stu &lulu,stu &caicai)  //全局函数重载+
{
    stu tem;
    tem.name=lulu.name+caicai.name;
    tem.num=lulu.num+caicai.num;
    tem.score=lulu.score+caicai.score;
    return tem;
}


int main()
{
    stu lulu("lulu",1,100);
    cout<<lulu<<endl;
    stu caicai("caicai",2,99);
    cout<<caicai+lulu<<endl;
    //stu kunkun;
    //cin>>kunkun;
    //cout<<kunkun<<endl;
    if(lulu==caicai)
    {cout<<"eq"<<endl;}
    else{cout<<"not eq"<<endl;}
    stu lulu2("lulu",1,100);
    if(lulu==lulu2)
    {cout<<"eq"<<endl;}
    else{cout<<"not eq"<<endl;}
}

重载++/–运算符

根据它们出现在所作用对象的前面还是后面来调用不同的函数,当编译器看到++a(前置++),它就调用operator++(a),当编译器看到a++(后置++),它就会去调用operator++(a,int).

++a(前置++),它就调用operator++(a),
a++(后置++),它就会去调用operator++(a,int)
–a(前置–),它就调用operator–(a),
a–(后置–),它就会去调用operator–(a,int)

重载后置++

类名称 operator++(int)
{
//先保存 旧的值old
//自增++
return old;//返回旧值
}

重载前置++

类名称 operator++()
{
//自增++
return *this;
}
#include<iostream>
#include<string>
using namespace std;
class stu{
    friend ostream& operator <<(ostream &cout,stu lulu);
private:
    string name;
    int num;
    float score;
public:
    stu(){}
    stu(string name,int num,float score):name(name),num(num),score(score){}
    stu operator ++(int)  //重载后置++,先输出值,在计算
    {
        stu old=*this;
        this->name=this->name+this->name;
        this->num++;
        this->score++;
        return old;
    }
    stu operator ++()//重载前置++,先计算,再输出值
    {
        this->name=this->name+this->name;
        this->num++;
        this->score++;
        return *this;
    }

};
ostream& operator <<(ostream &cout,stu lulu) //<<重载不要取引用,调用类的private,需要成为类的友元
{
    cout<<lulu.name<<" "<<lulu.num<<" "<<lulu.score<<endl;
    return cout; //cout类型为ostream,返回他,可以进行链式操作,返回的是一个引用
}
int main()
{
    stu lulu("lulu",1,100);
    stu caicai,kunkun;
    caicai=lulu++;
    kunkun=++lulu;
    cout<<caicai<<endl;
    cout<<kunkun<<endl;
}

重载函数调用运算符

重载()运算符 一般用于 为算法 提供策略。

当对象和()结合 会触发 重载函数调用运算符

#include <iostream>

using namespace std;
//仿函数
class Print
{
public:
    //重载函数调用运算符
    void operator()(char *str)
    {
        cout<<str<<endl;
    }
};
int main(int argc, char *argv[])
{
    Print ob;
    //对象和()结合触发operator()调用
    ob("hello Print");

    //Print()为匿名对象,没有对象名
    Print()("hello print");
    return 0;
}

智能指针

解决 堆区空间的对象 释放问题

#include <iostream>
using namespace std;
class Data
{
public:
    Data()
    {
        cout<<"Data的无参构造"<<endl;
    }
    ~Data()
    {
        cout<<"Data的析够"<<endl;
    }
    void func()
    {
        cout<<"Data的func函数"<<endl;
    }
};

//智能指针
class SmartPointer
{
private:
    Data *p;
public:
    SmartPointer(){}
    SmartPointer(Data *p)
    {
        this->p = p;
    }
    ~SmartPointer()
    {
        delete p;
    }
    // 重载->运算符
    Data* operator->()
    {
        return p;
    }
    Data& operator*()
    {
        return *p;
    }
};
int main(int argc, char *argv[])
{
    SmartPointer sp(new Data);

    //sp.operator ->()->func()相当于p->fun()
    sp->func();
    (*sp).func();

    return 0;
}

不要重载 && ||

不能重载operator&& 和 operator|| 的原因是,无法在这两种情况下实现内置操作符的完整语义。更具体一些,内置版本版本特殊之处在于:内置版本的&&和||首先计算左边的表达式,如果这完全能够决定结果,就无需计算右边的表达式了–而且能够保证不需要。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值