精华内容
下载资源
问答
  • 成员函数
    千次阅读
    2021-06-08 10:00:48

    1 C++类的成员函数

    1.1 C++类成员函数的性质

           类的成员函数是函数的一种,它的用法和作用和一般函数基本上是一样的,它也有返回值和函数类型。它与一般函数的区别只是:它是属于一个类的成员,出现在类体中。它可以被指定为private(私有的),public(公用的)或protected(受保护的)。

           在使用类成员函数的时候,要注意调用它的权限(即,它能否被调用)以及它的作用域(函数能使用什么范围中的数据和函数)。例如,私有的成员函数只能被本类中的其他成员函数所调用,而不能被类外调用。成员函数可以访问本类中任何成员(包括私有的和公用的),可以引用在本作用域中有效的数据。

           所以,定义类的成员函数时,可以将需要被外界调用的成员函数指定为public,它们是类的对外接口。但应注意,并非要求把所有成员函数都指定为public。有的函数并不是准备为外界调用的,而是为本类中的成员函数所调用的,就应该将它们指定为private。

           类的成员函数是类体中十分重要的部分,如果一个类中不包含成员函数,就等同于C语言中的结构体类,体现不出类在面向对象程序设计中的作用。

    1.2 在类外定义成员函数

           前面看到的成员函数是在类体中定义的,那么,也可以在类体中只写成员函数的声明,而在类外面进行函数定义,例如:

    class student       //class开头

    {

    private:    //以下的部分是私有部分

        char name[32];

        char addr[32];

        long long number;//以上3 行是成员变量

    public:     //以下部分是公有部分

        student(char* pn, char* pa, long long n)

        {

            strcpy(name, pn);

            strcpy(addr, pa);

            number = n;

        }

        void print();       //这是成员函数

    };

    void student::print()       //这是成员函数

    {

        cout << "name = " << name << endl;

        cout << "addr = " << addr << endl;

        cout << "number = " << number << endl;

    }

           注意:在类体中直接定义函数时,不需要在函数名前面加上类名,因为函数就属于当前定义该函数的类。但成员函数在类外定义的时候,必须在函数名前面加上类名,“::”是作用域限定符或称作用域运算符,用它声明函数是属于哪个类的。

    例如,student::print() 表示作用域为student类的print()函数,也就是student类中的print()函数。如果没有“student::”的限定,print()指的是它所在的作用域中的print()函数,就是一个普通的函数,不属于student类。对上面程序段而言,就是指全局作用域中的print()函数,而不是student类中的print()函数。而且不同的类中可能有同名的函数,用作用域限定符加以限定,就明确地说明了是哪一个作用域的函数,也就是哪一个类的函数。

           如果在作用域运算符“::”的前面没有类名,或者函数名前面既无类名,又无作用域运算符“::”,如:

    ::print()          或          print()

    就表示 print() 函数不属于任何类,这个函数不是成员函数,而是全局函数,即非成员函数的一般普通函数。

           类函数必须先在类体中作原型声明,然后,在类外定义,也就是说类体的位置应在函数定义之前(如上面所示的那样),否则编译时会出错。

           虽然函数在类外部定义,但在调用成员函数时会根据在类中声明的函数原型找到函数的定义(函数代码),从而执行该函数。

    韦凯峰 Linux C/C++ 程序设计教程,Linux 系统编程,Openwrt 系统开发,微信:13926572996,QQ:1523520001,博客:www.mylinux.vip

    更多相关内容
  • const成员函数

    千次阅读 2020-08-11 15:02:46
    const修饰成员函数的时候,const需要放在成员函数的后面,不能放在一开始,如果放在一开始的话,那么const其实是在修饰成员函数的返回值,而不是在修饰成员函数了 const成员函数中不能修改成员变量 普通成员函数中...

    const成员函数

    • const修饰成员函数的时候,const需要放在成员函数的后面,不能放在一开始,如果放在一开始的话,那么const其实是在修饰成员函数的返回值,而不是在修饰成员函数了
      在这里插入图片描述
    • const成员函数中不能修改成员变量
      在这里插入图片描述
    • 普通成员函数中可以修改成员变量
      在这里插入图片描述
    • 到底要不要使用const去修饰成员函数,就看你的函数中的变量需不需要被修改,如果希望其被修改的话,还是不要给成const,因为给成const的话,就不能去修改他了
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    const修饰类的成员函数
    • 将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改
      在这里插入图片描述
    看下面的代码
    class Date
    {
    public:
    	Date(int year = 1900, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    		cout << "Date(int,int,int):" << this << endl;
    	}
    
    	Date(const Date& d)
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    		cout << "Date(Date&):" << this << endl;
    	}
    
    	// d1 = d2 = d3;
    	Date& operator=(const Date& d)
    	{
    		if (this != &d)
    		{
    			_year = d._year;
    			_month = d._month;
    			_day = d._day;
    		}
    
    		return *this;
    	}
    
    	// 可读可写
    	// Date* const  this
    	void TestFunc1()
    	{
    		//this = nullptr;
    		this->_day++;
    	}
    
    	// 在该函数中不能修改当前对象的成员变量
    	// const修饰this指针
    	// const Date* const this;
    	void TestFunc2()const
    	{
    		this->_day++;
    		//_year++;
    		//_month++;
    	}
    
    	// 只读操作:const Date* const
    	void TestFuncWithConst()const
    	{
    		//TestFunc1(/*this*/);   // Date* const
    	}
    
    	// Date* const:可读可写
    	void TestFuncWithoutConst()
    	{
    		TestFunc2(); // const Date* const: 只读
    	}
    
    	// (1+2)*3 ---> 12+3*
    	~Date()
    	{
    		cout << "~Date():" << this << endl;
    	}
    
    	Date* operator&()
    	{
    		return this;
    	}
    
    	// Date* p = &d2;
    	const Date* operator&()const
    	{
    		return this;
    	}
    
    private:
    	int _year;
    	int _month;
    	mutable int _day;
    	//但是有些时候,我还是希望有些值是可以变化的
    	//加上const之后修改成员函数,那么成员函数里面所有的值都不能发生变化了
    	//如果我希望有些值仍然是可以修改的话,那么我可以再某些变量的前面加上mutable
    	//加上mutable之后就表明这个变量可以在const所修饰的成员变量中被修改
    };
    
    int main()
    {
    	Date d1(2019, 3, 24);
    	const Date d2(2019, 3, 25); // 常量:d2中的内容不允许被修改
    	cout << &d2 << endl;
    	//d2.TestFunc1();  // TestFunc1成员函数:普通的成员函数,在函数中可以修改当前对象的成员
    	d1.TestFunc1();
    	d1.TestFunc2();
    	return 0;
    }
    
    • 但是有些时候,我还是希望有些值是可以变化的,加上const之后修改成员函数,那么成员函数里面所有的值都不能发生变化了,如果我希望有些值仍然是可以修改的话,那么我可以再某些变量的前面加上mutable,加上mutable之后就表明这个变量可以在const所修饰的成员变量中被修改

    请思考下面的几个问题

    这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
    1. const对象可以调用非const成员函数吗?
    • 不可以,原因在于用const所修饰的变量本质上是一个常量,所以也就是说常量中的内容是不可以被修改的,但是普通的成员函数,在函数中可以修改当前对象的成员,所以两者是互相矛盾的,所以无法调用
      在这里插入图片描述
    1. 非const对象可以调用const成员函数吗?
    • 答案是可以的,因为普通类型的对象既可以修改成员变量的值,也可以不去修改成员变量的值,那到底要不要修改,根据情况而定就可以了。
      在这里插入图片描述
    1. const成员函数内可以调用其它的非const成员函数吗?
    • 是不可以的,因为如果你把一个函数声明为const类型的函数,那么就说明这个函数是只读的,是不可以修改的,而非const的成员函数是可读可写的
    1. 非const成员函数内可以调用其它的const成员函数吗?
    • 可以,因为外层函数的类型是 Date* const:可读可写,而内层函数的类型是const Date* const: 只读,外层可以修改也可以不修改,到底要不要修改,根据具体情况而定就可以了

    取地址及const取地址操作符重载

    // Date* p = &d2;
    const Date* operator&()const    //如何第一个const没有加的话,代码是不能通编译的
    {
    	return this; 
    	//因为要返回的是指针,但是如果返回的只是类类型的指针的话是不可以的,因为this指针是const类型的
    	//所以返回值的前面也要加上const才可以
    }
    int main()
    {
    	Date d1(2019, 3, 24);
    	const Date d2(2019, 3, 25); // 常量:d2中的内容不允许被修改
    	cout << &d2 << endl;
    	//d2.TestFunc1();  // TestFunc1成员函数:普通的成员函数,在函数中可以修改当前对象的成员
    	d1.TestFunc1();
    	d1.TestFunc2();
    	return 0;
    }
    
    class Date
    {
    public :
    Date* operator&()
    {
    	return this ;
    }
    const Date* operator&()const
    {
    	return this ;
    }
    private :
    	int _year ; // 年
    	int _month ; // 月
    	int _day ; // 日
    };
    
    • 这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容

    程序的运行结果是什么

    class Date
    {
    public:
    	Date(int year = 1900, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    		cout << "Date(int,int,int):" << this << endl;
    	}
    
    	Date(const Date& d)
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    		cout << "Date(Date&):" << this << endl;
    	}
    
    	// d1 = d2 = d3;
    	Date& operator=(const Date& d)
    	{
    		cout << this << "=" << &d << endl;
    		if (this != &d)
    		{
    			_year = d._year;
    			_month = d._month;
    			_day = d._day;
    		}
    
    		return *this;
    	}
    
    	~Date()
    	{
    		cout << "~Date():" << this << endl;
    	}
    
    
    	int _year;
    	int _month;
    	int _day;
    };
    
    Date TestDate(Date& d)
    {
    	Date temp(d);
    	temp._day += 1;
    	return d;
    }
    
    void TestDate()
    {
    	Date d1(2019, 3, 24);
    	Date d2(d1);
    	d1 = TestDate(d2);
    }
    
    int main()
    {
    	TestDate();
    	return 0;
    }
    
    • 打印结果如下所示:
      在这里插入图片描述
      在这里插入图片描述
    class Date
    {
    public:
    	Date(int year = 1900, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    		cout << "Date(int,int,int):" << this << endl;
    	}
    
    	Date(const Date& d)
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    		cout << "Date(Date&):" << this << endl;
    	}
    
    	// d1 = d2 = d3;
    	Date& operator=(const Date& d)
    	{
    		cout << this << "=" << &d << endl;
    		if (this != &d)
    		{
    			_year = d._year;
    			_month = d._month;
    			_day = d._day;
    		}
    
    		return *this;
    	}
    
    	~Date()
    	{
    		cout << "~Date():" << this << endl;
    	}
    
    
    	int _year;
    	int _month;
    	int _day;
    };
    
    Date& TestDate(Date& d)
    {
    	Date temp(d);
    	temp._day += 1;
    	return d;
    }
    
    void TestDate()
    {
    	Date d1(2019, 3, 24);
    	Date d2(d1);
    	d1 = TestDate(d2);
    }
    
    int main()
    {
    	TestDate();
    	return 0;
    }
    
    • 结果如下所示:
      在这里插入图片描述
    const 重载

    在这里插入图片描述

    • 上面的两个函数是可以形成重载的,之所以可以形成重载是因为有const进行修饰,因为一旦加了const进行修饰的话,两个函数的this指针的类型就不一样了,这也就是为什么两个函数可以形成重载的原因
    展开全文
  • 静态数据成员和静态成员函数

    万次阅读 多人点赞 2018-08-26 19:18:35
    c++中的static静态数据成员和静态成员函数应该是让大家比较头疼的东西,好像也是找工作公司面试中常常问到的东西。我自己也深有体会,在学习c++的过程中,总感觉static很烦人,但是又是一个必须懂的东西,所以今天就...

    转载自:https://blog.csdn.net/computer_liuyun/article/details/29235111

    c++中的static静态数据成员和静态成员函数应该是让大家比较头疼的东西,好像也是找工作公司面试中常常问到的东西。我自己也深有体会,在学习c++的过程中,总感觉static很烦人,但是又是一个必须懂的东西,所以今天就对静态数据成员和静态成员函数坐下小结哈!

    一、静态数据成员

    1.静态数据成员怎么去定义?

    在类中声明静态数据成员很简单,是以static关键字表明即可,如下所示

    class Test{
    private:
    	//静态数据成员
    	static int a;
    };
    

    a就是静态数据成员了,在类中只能声明可是不能定义哈!

    要对静态数据成员定义和初始化必须在类的外面也就是在全局作用域中定义,如果定义不给出初值,则默认初值为0

    class Test{
    public:
    	int GetA() const{return a;}
    private:
    	//静态数据成员
    	static int a;
    };
    //int Test::a;如果这样定义不赋予初值则初值为零
    int Test::a = 1;
    #include <iostream>
    int main()
    {
    	Test T;
    	std::cout << T.GetA() << std::endl;
    }
    

    定义时一定要在全局作用域中定义,一定不要在类中定义!

    静态数据成员甚至在类没有任何对象的时候都可以访问,静态成员可以独立访问,无需依赖任何对象的建立

    另外,不要试图在头文件中定义(初始化)静态数据成员。在大多数的情况下,这样做会引起重复定义这样的错误。即使加上#ifndef #define #endif或者#pragma once也不行。

     

    2.静态数据成员被类的所有对象共享,包括该类的派生类对象,基类对象和派生类对象共享基类的静态数据成员

    答:静态数据成员不属于任何对象,类的静态数据成员的存在不依赖于任何类对象的存在,静态数据成员是由类的所有对象共享的。例子如下:

    class Base{
    public:
    	//静态数据成员
    	static int a;
    };
    class Derived : public Base{
     
    };
    //int Test::a;如果这样定义不赋予初值则初值为零
    int Base::a;
    #include <iostream>
    int main()
    {
    	Base B;
    	Derived D;
    	B.a++;
    	std::cout << B.a << std::endl;
    	D.a++;
    	std::cout << D.a << std::endl;
    	
    }
    

    打印结果如下:

    由打印结果看出来,派生类对象和基类对象都是共享基类的静态数据成员,而基类的所有对象也是共享该静态数据成员,且该静态数据成员应该在全局作用域中定义,可以赋予初值也可以不赋予初值,如果不赋予初值,静态数据成员有其默认值。

     

    3.静态数据成员可以作为成员函数的默认形参,而普通数据成员则不可以

    答:不多说,直接看例子马上就明白了哈!

    class Test{
    public:
    	//静态数据成员
    	static int a;
    	int b;
    	void fun_1(int i = a);//正确
    	void fun_2(int i = b);//报错
    };
    

    4.静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。普通数据成员的只能声明为 所属类类型的 指针或引用

    答:这个也不多说,直接看例子就可以懂什么意思哈!

    class Test{
    public:
    	//静态数据成员
    	static Test a;//正确
    	Test b;//报错
    	Test *pTest;//正确
    	Test &m_Test;//正确
    	static Test *pStaticObject;//正确
    };
    

    5.静态数据成员在const函数中可以修改,而普通的数据成员是万万不能修改的哈!

    答:看个例子

    class Test{
    public:
    	Test():b(0){}
    	//静态数据成员
    	static int a;//正确
    	int b;
    	void test() const
    	{
    		a++;
    		b++;//const指的不能修改当前调用该函数对象的数据成员
    	}
    };
    int Test::a;
    

    const修饰的时当前this指针所指向的对象是const,但是静态数据成员不属于任何类的对象,它被类的所有对象修改,所以this指针不修饰静态的数据成员,所以可以更改。

     

    二、静态成员函数

    静态成员函数的声明也很简单,就是在类的成员函数前加上static关键字即可,和静态成员一样,静态成员函数也是属于类的,它并不属于任何对象,当调用静态成员函数时应该使用类名和域运算符“∷”,当然也可以使用对象调用操作,但是这样的操作并不意味着静态成员函数属于这个对象,它只是被这个对象共享而已,这样也就决定了静态成员函数中是不能访问本类中的非静态数据成员的,它是不能访问非静态数据成员的,在c++中静态成员函数主要用来访问静态数据成员而不访问非静态数据成员

    1.静态成员函数不能调用非静态成员函数,但是反过来是可以的

    2.静态成员函数没有this指针,也就是说静态成员函数不能使用修饰符(也就是函数后面的const关键字)

    3.静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用 类成员函数指针来储存。

    总结:其实声明为静态,不论是静态数据成员还是静态成员函数,它们都是不依赖于对象而存在的,类在定义后并不分配存储空间,而是在定义类的对象的时候才分配存储空间,相反静态的数据成员和静态的成员函数是已经在内存中开辟了内存空间了,所以静态数据成员可以独立的访问在任何类对象没有建立起来都可以访问,并且静态成员函数不可以调用非静态成员函数,因为非静态成员函数只有在类对象建立以后才可以调用,相反则是可以的。我觉得当对某个判断产生怀疑的时候应该去测试,只有验证了才知道是不是对的哈!

    为了能更好阐释静态数据成员和静态成员函数,然后在网上搜了博客,感觉有些例子不错(因找不到最初的出处,所以无法注明请原作者谅解),所以就拿来给大家参考一下哈!

    关于玩篮球的例子很能明显解释静态数据成员和静态成员函数到底是怎么回事

    你们班里面有10个人(10个比如高一一班的对象),体育老师分给你们一个篮球(静态成员函数),你们每个人都带了一个篮球(非静态成员数),你们都很小气,自己的球只能自己拍,要是5对5打比赛,那就只能用那个静态的篮球了(每个人都可以拿来用,但是带来的影响是对全体的)。因此,我可以说那个篮球是高一一班的成员。所以也就是说:静态成员函数是类的成员函数(因为高一二班就不能拿来玩),但是这个篮球最后还是要还给老师的,任何私人不得占有。希望这样你能明白,其实在内存空间里面说白了静态的成员的内存是唯一的一份,就是当你在类外声明他时开辟的,但是非静态函数的空间分配是在你实例化对象时创建的。

    为了使大家更好的理解这两个概念,还是老样子用代码来说明上面文字说明的这一切哈!

    关于学生类的例子

    //定义Student类
    #include <iostream>
    class Student                  
    {
    public:
    //定义构造函数
    Student(int n,int a,float s):num(n),age(a),score(s){ }      
    void total();
    //声明静态成员函数
    static float average();      
    private:
    	int num;
    	int age;
    	float score;
    	//静态数据成员,累计学生的总分
    	static float sum; 
    	//静态数据成员,累计学生的人数
    	static int count;           
    };
    //在全局作用域对静态数据成员初始化,如果不赋予初值,则使用其默认值零
    float Student::sum;                     
    int Student::count;
    //定义非静态成员函数
    void Student::total()                    
    {
    	//累加总分
    	sum+=score;
    	//累计已统计的人数
    	count++;                               
    }
    //定义静态成员函数
    float  Student::average()                  
    {
    	return(sum/count);
    }
    int main()
    {
    	Student stud[3]={    
    	//定义对象数组并初始化
    	Student(1001,18,70),
    	Student(1002,19,78),
    	Student(1005,20,98)
    };
    int n;
    std::cout<<"please input the number of students: ";
     //输入需要求前面多少名学生的平均成绩
    std::cin>>n;                              
    //调用3次total函数
    for(int i=0;i<n;i++)
    {
    	stud[i].total();
    }
    //调用静态成员函数
    std::cout<<"the average score of "<<n<<" students is "<<Student::average( )<<std::endl;
    return 0;
    }
    

    打印输出如下:

     

    对上面的代码做以下说明:

    首先,在主函数中定义了对象数组,存放每个学生的编号、年龄和成绩,其中sum和count是要累计所有学生的总成绩和总的学生人数,我们定义成了静态数据成员,在学生类的成员函数中,我们定义了普通的total成员函数,用来计算所有学生的总成绩和总的学生人数,另外,定义了静态成员函数average,学生类的设计大概如此

    在全局作用域对类中静态数据成员进行了定义,但未赋予初值,这意味着我们采用其默认值。

    在类的普通成员函数toal中可以引用静态数据成员sum和count,可见类的所有成员函数都可以引用类的静态数据成员。

    在类的静态成员函数average中,只能引用类的静态数据成员,不能引用非静态数据成员。

    在主函数中我们调用类的非静态成员函数时只能通过类对象来调用,如stu[i].total,但是对于静态成员函数可以直接通过类名+作用域符号来调用,如

    Student::average。

     

    展开全文
  • C++ 运算符重载:成员、非成员函数重载

    千次阅读 多人点赞 2020-06-19 19:41:34
    C++ 运算符重载运算符重载1、背景2、运算符函数重载的两种形式1、成员函数重载1、定义格式2、非成员函数重载(友元)1、定义格式3、重载原则4、参数和返回值5、成员函数重载1、双目运算符重载1、定义2、调用格式2、...

    一、运算符重载

    1、背景

    • C++中标准运算符(如+、—、*、/、<、>等)的操作对象只能是基本数据类型。但对于用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,使它能够用于特定类型执行特定的操作。
    • 运算符重载,本质上是函数重载,属于静态多态

    2、运算符函数重载的两种形式

    1、成员函数重载

    1、定义格式

    • 成员函数声明的格式:
    函数类型 operator 运算符(参数表);
    
    • 成员函数定义的格式:
    函数类型 类名::operator 运算符(参数表)
     {
     	 函数体;
      }
    

    2、非成员函数重载(友元)

    1、定义格式

    • 友元函数声明的格式:
    friend 函数类型 operator 运算符(参数表);
    
    • 友元函数定义的格式:
     函数类型 operator 运算符(参数表)
    {
      	函数体;
     }
    

    3、重载原则

    • (1) 除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载。
    • (2) 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
    • (3) 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
    • (4) 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
    • (5) 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
    • (6) 运算时,有数和对象的混合运算时,必须使用友元
    • (7) =,(),[],->不能以友元方式重载
    • (8) 二元运算符中,第一个操作数为非对象时,必须使用友元函数。如输入输出运算符<<和>>
      为什么输出运算符重载不能是一个成员函数?而非得声明为友元?
      原因如下:
     返回值 operator运算符(参数列表){}
    

    重载运算符时,函数声明在类内和类外是有区别的,比方说 + - * / 等需要2个操作数的运算符,当声明在类的外部时,则参数列表为2个参数,第一个参数为运算符左边的操作数,而第二个参数为操作符右边的操作数:如下

    classType operator+(classType& left, classType& right);
    

    而当函数声明在类的内部时,即为类的成员函数时,

    classType operator+(classType& right );
    

    第一个操作数就是调用该操作符的对象的引用第二个操作数是传进来的参数,所以,如果要重载<<运算符,一般写法是这样的

    ostream& operator<<(ostream& os, const classType& obj);
    

    第一个参数是该运算符的第一个操作数,然而,却不是类对象
    所以当该类运算符重载时写在类的内部时,又为了访问类内除public外的其它变量或函数,
    则应当声明为友元函数:

    friend ostream& operator<<(ostream& os, const classType& obj);
    

    所以,为什么有些运算符的重载要声明为友元函数,是类似的。

    运算符重载原则表:

    运算符建议使用方式
    一元运算符成员函数
    = ( ) [ ] ->必须是成员函数
    += -= /= *= ^= &= != %= >>= <<=成员函数
    所有其它二元运算符, 例如: –,+,*,/友元函数
    << >>友元函数

    4、参数和返回值

    • 参数:
      当参数不会被改变,一般按const引用来传递(若是使用成员函数重载,函数也为const).

    • 返回数值:

    1. 如果返回值可能出现在=号左边, 则只能作为左值, 返回非const引用。
    2. 如果返回值只能出现在=号右边, 则只需作为右值, 返回const型引用或者const型值。
    3. 如果返回值既可能出现在=号左边或者右边, 则其返回值须作为左值, 返回非const引用。

    5、成员函数重载

    定义格式

    <函数类型> operator <运算符>(<参数表>)
        {
         <函数体>
        }
    

    1、双目运算符重载

    1、定义

    双目运算符(或称二元运算符)是C++中最常用的运算符。双目运算符有两个操作数,通常在运算符的左右两侧,

    +,-,*,/,%,<,>,>=,<=,==,!=,<<,>>,&,^,|,&&,||,=

    如3+5,a=b,i<10等

    注意:
    双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数,因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。

    2、调用格式

    <对象名>.operator <运算符>(<参数>)  //obj1.operator<运算符>(OBJ obj2)
    等价于
    <对象名><运算符><参数>   //obj1<运算符>obj2
    
    例如  a+b  等价于   a.operator+(b)
    

    2、单目运算符重载

    1、定义

    单目运算符是指运算所需变量为一个的运算符,即在运算当中只有一个操作数,又叫一元运算符。

    逻辑非运算符:!、按位取反运算符:~、自增自减运算符:++, - - 等

    2、调用格式

    • 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。
    <运算符> obj    
    相当于
    obj.operator<运算符>();
    
    例如:
    ++a 相当于 a.operator++();
    
    • 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。
    obj <运算符>  
    相当于
    obj.operator<运算符>(int);
    
    例如:
    a++ 相当于 a.operator++(int);
    

    6、非成员函数(友元)重载

    1、定义

    通常我们都将其声明为友元函数,因为大多数时候重载运算符要访问类的私有数据,如果不声明为类的友元函数,而是通过在此函数中调用类的公有函数来访问私有数据会降低性能。所以一般都会设置为类的友元函数,这样我们就可以在此非成员函数中访问类中的数据了。

    2、定义形式

    声明的格式:

    friend 函数类型 operator 运算符(参数表);
    

    定义的格式:

     friend 函数类型 operator 运算符(参数表){
      函数体;
      }
    

    3、调用格式

    1. 双目运算符 B重载后,
    obj1 运算符 obj2   等同于   operator   运算符(obj1,obj2 )
    
    1. 前置单目运算符 B重载后,
     运算符 obj   等同于    operator 运算符(obj )
    
    1. 后置单目运算符 ++和–重载后,
     obj 运算符  等同于   operator 运算符(obj,0 )
    

    举例

    //前置++运算符重载
    friend 函数类型 & operator++(类类型 &);
    //后置++运算符重载
    friend 函数类型 & operator++(类类型 &,int);
    

    4、重载原则:

    • 函数的形参代表依自左至右次序排列的各操作数。
    • 参数个数=原操作数个数(后置++、–除外)
    • 至少应该有一个自定义类型的参数。
    • 后置单目运算符 ++和–的重载函数,形参列表中要增加一个int,但不必写形参名。
    • 如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元。

    7、 +和 -运算符的重载实例

    //https://blog.csdn.net/zhuzhaoming1994/article/details/80371779
    class Point  
    {  
    private:  
    	int x; 
    public:  
    	Point(int x1)
    	{  	x=x1;}  
    	Point(Point& p)   
    	{  	x=p.x;}
    	const Point operator+(const Point& p);//使用成员函数重载加号运算符
    	friend const Point operator-(const Point& p1,const Point& p2);//使用友元函数重载减号运算符
    };  
     
    const Point Point::operator+(const Point& p)
    {
    	return Point(x+p.x);
    }
     
    const Point operator-(const Point& p1,const Point& p2)
    {
    	return Point(p1.x-p2.x);
    }
    

    结果:

    Point a(1);  
    Point b(2);
    a+b;  //正确,调用成员函数
    a-b;  //正确,调用友元函数
    a+1;  //正确,先调用类型转换函数,把1变成类类型,之后调用成员函数
    a-1;  //正确,先调用类型转换函数,把1变成类类型,之后调用友元函数
    1+a;  //错误,调用成员函数时,第一个操作数必须是对象,因为第一个操作数还有调用成员函数的功能
    1-a;  //正确,先类型转换 后调用友元函数
    

    8、++和–运算符的重载实例

    //https://blog.csdn.net/zhuzhaoming1994/article/details/80371779
    class Point  
    {  
    private:  
    	int x; 
    public:  
    	Point(int x1)
    	{  	x=x1;}  
    	Point& operator++();//成员函数定义自增
    	
    	/*函数返回临时变量,不能出现在等号的左边(临时变量不能做左值),函数的结果只能做右值,则要返回一个const类型的值*/
    	const Point operator++(int x); //后缀可以返回一个const类型的值,这里引入一个虚参数int x,x可以是任意整数。
    	friend Point operator--(Point& p);//友元函数定义--
    	friend const Point operator--(Point& p,int x);//后缀可以返回一个const类型的值
    };  
     
    Point Point::operator++()//++obj
    {
    	x++;
    	return *this;
    }
    const Point Point::operator++(int x)//obj++
    {
    	Point temp = *this;
    	this->x++;
    	return temp;
    }
    Point operator--(Point& p)//--obj
    {
    	p.x--;
    	return p;
             //前缀形式(--obj)重载的时候没有虚参,通过引用返回*this 或 自身引用,也就是返回变化之后的数值
    }
    const Point operator--(Point& p,int x)//obj--
    {
    	Point temp = p;
    	p.x--;
    	return temp;
             // 后缀形式obj--重载的时候有一个int类型的虚参, 返回原状态的拷贝
    }
    
    Point a(1);
    Point b(2);
    a++;//隐式调用成员函数operator++(0),后缀表达式
    ++a;//隐式调用成员函数operator++(),前缀表达式
    b--;//隐式调用友元函数operator--(0),后缀表达式
    --b;//隐式调用友元函数operator--(),前缀表达式
    cout<<a.operator ++(2);//显式调用成员函数operator ++(2),后缀表达式
    cout<<a.operator ++();//显式调用成员函数operator ++(),前缀表达式
    cout<<operator --(b,2);//显式调用友元函数operator --(2),后缀表达式
    cout<<operator --(b);//显式调用友元函数operator --(),前缀表达式 
    

    9、重载输入输出操作符<< >>实例

    class Point  
    {  
    private:  
    	int x; 
    public:  
    	Point(int x1)
    	{  	x=x1;} 
    	friend ostream& operator<<(ostream& cout,const Point& p);//使用友元函数重载<<输出运算符
    	friend istream& operator>>(istream& cin,Point& p);//使用友元函数重载>>输出运算符
    };  
    ostream& operator<<(ostream& cout,const Point& p)
    {
    	cout<<p.x<<endl;
    	return cout;
    }
    istream& operator>>(istream& cin,Point& p)
    {
    	cin>>p.x;
    	return cin;
    }
    
    • 语法:
      重载方式:只能使用友元函数重载 且 使用三个引用&

    • 函数名:

      输出流: operator<<(参数表)
      输入流:operator>>(参数表)
      
    • 参数表:两个参数均用引用&

      输出流: 必须是两个参数:对输出流ostream& 和 对象
                       第一个操作数cout,定义在文件iostream中,是标准类类型ostream的对象的引用。
                       如:ostream& cout,const Point& p
      输入流:必须是两个参数:对输入流ostream& 和 对象
                      第一个操作数是cin,定义在文件iostream,实际上是标准类类型istream的对象的引用
                      如:instream& cin,const Point& p
      
    • 函数调用:

      输出流: 显式调用:cout<<对象
                       隐式调用: operator<<(cout,对象)
      输入流:显式调用:cin>>对象
                       隐式调用: operator>>(cin,对象)
      

    返回类型:返回类型固定 + 使用返回函数引用(满足连续输出)

       输出流: 返回ostream&
                        如:ostream& operator<<(ostream& cout,const Point& p)
       输入流:返回:istream&
                        如:istream& operator>>(istream& cin,Point& p)
    

    10、类型转换运算符重载

    类型转换函数的作用是将一个类的对象转换成另一类型的数据。(C++中没有返回类型的函数有3个,构造函数、析构函数、类型转换函数。)

    • 1、必须是成员函数,不能是友元函数 ,因为转换的主体是本类的对象。不能作为友元函数或普通函数。
    • 2、没有参数(操作数是什么?)
    • 3、不能指定返回类型(返回值的类型是由函数名中指定的类型名来确定)
    • 4、函数原型:
    operator 类型名( )
        {
            实现转换的语句
        }
    

    举例:
    类型转换运算符,只要你把XXX对象隐式或者显式转换为T对象时,它都会被自动调用。

    //https://www.cnblogs.com/renyuan/p/6555172.html
    #include<iostream>  
    using namespace std;  
    //类型转换运算符重载,只要你把XXX对象隐式或者显式转换为T对象时,它自动被调用  
      
    template<class T>  
    class Transfer  
    {  
    public:  
        Transfer(int arg):i(arg){}  
        operator T() const  
        {  
            return i;  
        }  
    private:  
        int i;  
    };  
      
    int main()  
    {  
        Transfer<double> t(3);  
        //double d =static_cast<double>(t);//显示转换  
        double d = t;//隐式转换  
        cout<<d;  
        getchar();  
        return 0;  
    }
    

    11、类成员访问运算符 -> 重载

    类成员访问运算符( -> )可以被重载,它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。

    #include<iostream>
    
    using namespace std;
    class DBHelper//动态分配内存更加方便
    {
    public:
    	DBHelper()
    	{
    		cout << " DBHelper()...." << endl;
    	}
    	~DBHelper()
    	{
    		cout << " ~DBHelper()" << endl;
    		//Close();
    	}
    	void Open()
    	{
    		cout << " Open()...." << endl;
    	}
    	void Query()
    	{
    		cout << " Query()...." << endl;
    	}
    	void Close()
    	{
    		cout << " Close()...." << endl;
    	}
     };
    
    class DB
    {
    public:
    	DB()
    	{
    		db_ = new DBHelper;//利用类对象确定性析构的特性
    	}
    	~DB()
    	{
    		delete db_;//利用类对象确定性析构的特性,将所包装的对象销毁掉
    	}
    	DBHelper* operator->()//重载指针运算符,返回的是DBHelper对象
    	{
    		return db_;
    	}
    
    private:
    	DBHelper* db_;
    };
    int main()
    {
    	DB db;
    	db->Open();
    	db->Query();
    	db->Close();
    	return 0;
    }
    

    12、string类实例(重载综合说明)

    String.h

    #ifndef _STRING_H_
    #define _STRING_H_
    #include<iostream>
    
    using namespace std;
    
    class String
    {
    public:
    	String(const char* str="");
    	String(const String& other);
    
    	String& operator=(const String& other);
    	String& operator=(const char* str);
    	bool operator!() const;
    
    	char& operator[](unsigned int index);
    	const char& operator[](unsigned int index) const;
    
    	friend String operator+(const String& s1, const String& s2);
    	//String operator+(const String& s1, const String& s2);
    	String& operator+=(const String& other);
    
    	friend ostream& operator<<(ostream& os, const String& str);
    	friend istream& operator>>(istream& is,  String& str);
    	~String(void);
    
    	void Display() const;
    
    private:
    	String& Assign(const char* str);
    	char* AllocAndCpy(const char* str);
    	char* str_;
    };
    
    #endif // _STRING_H_
    
    

    String.cpp

    #pragma warning(disable:4996)
    #include "String.h"
    #include <string.h>
    //#include <iostream>
    //using namespace std;
    
    String::String(const char* str)
    {
    	str_ = AllocAndCpy(str);
    }
    
    String::String(const String& other)
    {
    	str_ = AllocAndCpy(other.str_);
    }
    
    String& String::operator=(const String& other)
    {
    	if (this == &other)
    		return *this;
    
    	/*delete[] str_;
    	str_ = AllocAndCpy(other.str_);
    	return *this;*/
    	return Assign(other.str_);
    }
    
    String& String::operator=(const char* str)
    {
    	/*delete[] str_;
    	str_ = AllocAndCpy(str);
    	return *this;*/
    	return Assign(str);
    }
    String& String::Assign(const char* str)
    {
    	delete[] str_;
    	str_ = AllocAndCpy(str);
    	return *this;
    }
    char& String::operator[](unsigned int index)
    {
    	return const_cast<char&>(static_cast<const String&>(*this)[index]);
    	//return str_[index];
    }
    
    const char& String::operator[](unsigned int index) const
    {
    	return str_[index];
    }
    bool String::operator!() const
    {
    	return strlen(str_) != 0;
    }
    
    
    
    char* String::AllocAndCpy(const char* str)
    {
    	int len = strlen(str) + 1;
    	char* newstr = new char[len];
    	memset(newstr, 0, len);
    	strcpy(newstr, str);
    
    	return newstr;
    }
    
    String operator+(const String& s1, const String& s2)
    {
    	//int len = strlen(s1.str_) + strlen(s2.str_) + 1;
    	//char* newchar = new char[len];
    	//memset(newchar, 0, len);
    	//strcpy(newchar, s1.str_);
    	//strcat(newchar, s2.str_);
    
    	//String tmp(newchar);//return String(newchar);内部构造函数分配空间,newchar没办法销毁
    	//delete newchar;
    	//return tmp;
    	String str = s1;
    	str += s2;
    	return str;
    }
    
    
    String& String::operator+=(const String& other)
    {
    	int len = strlen(str_) + strlen(other.str_) + 1;
    	char* newchar = new char[len];
    	memset(newchar, 0, len);
    	strcpy(newchar, str_);
    	strcat(newchar, other.str_);
    
    	delete str_;
    	str_ = newchar;
    	return *this;
    }
    
    //cout 类型就是ostream,第一个参数是ostream,不是对象自身,所以选择友元方式重载
    //返回值还是ostream&,保证接下来的对象还能够被继续输出,
    //例如 cout << str << endl;//cout << str返回值还是ostream,所以可以继续输出<< endl
    ostream& operator<<(ostream& os, const String& str)
    {
    	os << str.str_;
    	return os;
    }
    
    istream& operator >> (istream& is, String& str)
    {
    	char tmp[1024];
    	cin >> tmp;
    	str = tmp;
    	return is;
    }
    
    void String::Display() const
    {
    	cout << str_ << endl;
    }
    
    String::~String()
    {
    	delete[] str_;
    }
    

    main.cpp

    #include "String.h"
    //#include<iostream>
    //
    //using namespace std;
    
    int main()
    {
    	String s1("abcdefg");
    	//s1.Display();
    
    	char ch = s1[2];
    	//cout << ch << endl;
    	s1[2] = 'A';
    	//s1.Display();
    
    	const String s2("abcdefgh");
    	//s2[2] = 'C';
    	//s2.Display();
    
    	String s3 = "xxx";//String(const char* str="");前面不能加explicit,需要隐式转化
    	String s4 = "yyy";
    	String s5 = s3 + s4;
    	s5.Display();
    	//如果以函数方式重载,不允许,因为成员函数隐含的一个参数是对象自身,不能够以"aaa"任意字符串开头
    	String s6 = "aaa" + s3;//不予许"bbb" + "aaa".因为String operator+(const String& s1, const String& s2)。
    	s6.Display();
    
    	s3 += s4;
    	s3.Display();
    
    	cout << s3 << endl;
    
    	String s7;
    	cin >> s7;
    	cout << s7 << endl;
    	return 0;
    }
    

    参考

    1、https://blog.csdn.net/zgl_dm/article/details/1767201
    2、https://blog.csdn.net/insistGoGo/article/details/6626952
    3、https://blog.csdn.net/zhuzhaoming1994/article/details/80371779
    4、https://www.cnblogs.com/xiaokang01/p/9166745.html
    5、https://www.cnblogs.com/renyuan/p/6555172.html

    展开全文
  • C/C++编程:成员函数的调用方式

    千次阅读 2021-05-05 17:11:49
    问题:对于Point3d指针和对象的函数调用 Point3d obj Point3d *ptr = &obj; obj.normalize(); ptr->normalize() 会发生什么事情呢?其中normalize()定义如下: Point3d Point3d::normalize() const{ float...
  • c++类成员函数指针

    千次阅读 2021-01-19 18:04:56
    提出疑问 首先问大家一句,什么是函数指针? 肯定有的人会这样回答,函数指针?...因为成员函数包括了虚函数和非虚函数(这里涉及虚表问题,可以先简单看看列出的虚函数系列,否则接下来问题会有点难以接受。) 虚函数
  • C++类成员函数指针使用介绍

    万次阅读 多人点赞 2019-09-21 16:07:49
    在之前写过的博客中有介绍过函数指针和指针函数的区别和简单用法(文章在这里),当时的Demo非常简单,都是C语言的写法,但是当在C++中直接像C那样使用类成员函数指针时就会报错:reference to non-static member ...
  • 类的成员函数(简称类函数)是函数的一种,它的用法和作用和前面介绍过的函数基本上是一样的,它也有返回值和函数类型,它与一般函数的区别只是:它是属于一个类的成员,出现在类体中。它可以被指定为private(私有的...
  • 类的6个默认成员函数

    千次阅读 2021-08-14 17:55:57
    深浅拷贝,拷贝构造函数,赋值运算符重载,构造函数,析构函数
  • 1、static成员函数不能访问非static成员函数/成员,只能static成员间相互访问: //.h class A { public: static func1() // 编译出错 { menber1 = 1; } static func3() { cout<<"abc"<<endl; } ...
  • 1、static 修饰成员变量 静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间。 静态成员变量必须在类中声明,在类外定义。 静态数据成员不属于某个对象,在为对象分配空间中不包括静态成员所占...
  • 【C++】成员函数

    万次阅读 多人点赞 2019-06-02 19:48:42
    C++成员函数(Member Functions) 目录 C++成员函数(Member Functions) 1、成员函数定义(Member Function Definition) 2、内联(Inline) 3、常成员函数(Const Member Functions) 4、使用对象指针...
  • 浅谈C++类中6个成员函数

    千次阅读 多人点赞 2021-03-18 15:45:13
    六个默认的成员函数构造函数浅谈深挖析构函数浅谈深挖拷贝构造函数浅谈深挖赋值重载函数浅谈深挖取地址重载函数const修饰的取地址重载函数二级目录 构造函数 浅谈 构造函数是一个特殊的成员函数,名字与类名相同且不...
  • (3) 重载运算符: 作为成员函数还是非成员函数 对于很多运算符来说,可以选择使用成员函数或非成员函数来实现运算符重载。 一般来说,非成员函数应是友元函数,这样它才能直接访问类的私有数据。 例如,Time类的...
  • 首先我们定义一个类Ctest,类里面包含三个不同形式的成员函数,静态成员函数statFunc()、动态成员函数dynFunc()和虚拟函数virtFunc()。在main函数中我们利用cout标准输出流分别输出这三个函数的地址,程序如下所示:...
  • c++私有成员函数 私人会员功能 (Private Member Function) A function declared inside the class's private section is known as "private member function". A private member function is accessible through the...
  • 成员函数的定义

    千次阅读 2020-05-06 17:03:55
    成员函数的定义 如上所述, 类中含有两种成份,即数据成员和成员函数, 其中成员函数也称方法。实际上,成员函数和方法指的是同一种实体, 是一种实体的两种不同叫法, 成员函数是程序设计语言 C + + 中的术语,而方法是...
  • 类的成员函数(简称类函数)是函数的一种,它的用法和作用和前面介绍过的函数基本上是一样的,它也有返回值和函数类型,它与一般函数的区别只是:它是属于一个类的成员,出现在类体中。它可以被指定为private(私有的...
  • 在类模板(及其成员函数)的定义中,我们将模板参数当作替身,代替使用模板时用户提供的类型或值。 代码示例: template <typename T> //用T这个模板类型参数,来表示A中保存的元素的类型。 //当用户实例化A...
  • C调用C++类成员函数--实例

    千次阅读 多人点赞 2022-04-29 17:54:55
    而C调用C++则是使用相对较少的,我们通过一个例子来说明C如何调用C++成员函数。 首先我们实现一个简单的C++程序,作为实验目标。 1. add.h #ifndef ADD_H #define ADD_H class addCode { public: addCode(); ...
  • [c++] 常成员函数

    千次阅读 多人点赞 2020-02-19 12:42:25
    成员函数声明: 返回类型 成员函数名(参数表) const ; 例如: int function(int x) const ; 常成员函数的主要特点: 1)不能更新类的成员变量 2)不能调用该类中没有用const修饰的成员函数,即只能调用常...
  • C++静态成员函数访问非静态成员的四种方法

    千次阅读 多人点赞 2021-07-01 17:00:48
    大家都知道C++中类的成员函数默认都提供了this指针,在非静态成员函数中当你调用函数的时候,编译器都会“自动”帮你把这个this指针加到函数形参里去。当然在C++灵活性下面,类还具备了静态成员和静态函数,即 class...
  • C++的const成员函数

    千次阅读 2021-03-09 23:51:05
    C++的const成员函数const成员函数是什么?实例总结 const成员函数是什么? 通常我们看到的const成员函数格式类似于: int QueryBalance(int iBalanceVal) const; 简单的说,const成员函数是类的常类成员,它能...
  • 运算符重载 对于面向对象的程序设计来说,运算符重载可以完成两个对象之间的复杂操作...为了重载运算符,首先要定义运算符重载函数,它通常是类的非静态成员函数或者友元函数,运算符的操作数通常也应为对象。 定...
  • C++类static成员函数的调用

    千次阅读 2021-10-13 18:08:31
    class中的静态成员函数作用再整个类的内部,对应类的所有实例是共享静态成员函数的,在调用静态成员函数的时候跟调用非静态成员函数是有区别的。另外,静态成员函数只能访问对应类内部的静态数据成员,否则会出现...
  • c++类成员函数做函数参数

    千次阅读 2020-10-26 17:47:29
    类内部的typedef函数声明,属于类成员,在类外声明时必须加类声明作用域(Test::FUNC),且赋值的函数必须为类成员(&Test::Func1) 下面的类中,Func1和Func2的返回值类型和参数列表都一样,定义的FUNC类似委托类型...
  • C++的静态成员变量和静态成员函数

    万次阅读 2019-05-22 15:35:41
    与普通的成员变量和成员函数相比,静态成员函数和静态成员变量是属于类的,而不是属于对象的,也就是说,在类实例化为对象之前,静态成员变量和静态成员函数就已经分配了内存空间了,而普通成员函数和成员变量只有在...
  • This指针,静态成员函数和非静态成员函数

    千次阅读 多人点赞 2019-02-27 14:12:12
    C++中,静态成员函数不能被声明为virtual函数。  例如,下面的程序会编译失败。 #include&lt;iostream&gt; class Test { public: // 编译错误:static成员函数不能声明为virtual virtual static ...
  • std::bind(二):包装成员函数

    千次阅读 2019-08-20 14:52:51
    文章目录前言普通函数嵌套包装包装类成员成员函数的包装成员变量的包装总结完整代码 前言 关于std::bind()对普通函数的包装作用,在之前的总结文章《std::bind(一):包装普通函数》已经举例说明过了,后来发现丢...
  • 转自:http://blog.chinaunix.net/uid-10673338-id-2936852.html 转自:...   对以上两篇文章,我添加了自己已有的部分知识,并重新地汇总整理 ... ...从函数定义的位置来粗略理解

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,239,835
精华内容 495,934
关键字:

成员函数

友情链接: 【9】第九章.zip