精华内容
下载资源
问答
  • public class TTTTT extends SuperC{ public String get(){ return null; } } class SuperC{ Object get(){ return null;...方法重载(overload): 1.必须是同一个类 2方法名(也可以...

    public class TTTTT extends SuperC{
        public String get(){
            return null;
        }
    }
    class SuperC{
        Object get(){
            return null;
        }
    }
    方法重载(overload):
    1.必须是同一个类
    2方法名(也可以叫函数)一样
    3参数类型不一样或参数数量不一样
    
    方法的重写(override)两同两小一大原则:
    方法名相同,参数类型相同
    子类返回类型小于等于父类方法返回类型,
    子类抛出异常小于等于父类方法抛出异常,
    子类访问权限大于等于父类方法访问权限。
    

      

    转载于:https://www.cnblogs.com/sunyubin/p/9751442.html

    展开全文
  • 重载

    千次阅读 2016-05-11 18:54:01
    函数重载 一、什么是函数重载? 函数重载overload是指不同的函数采用相同的函数名,彼此间通过形参列表加以区分。 举例: 函数名都为distance,但形参列表的个数不同; #include #include using namesapce ...

    • 函数重载


    一、什么是函数重载?

    函数重载overload是指不同的函数采用相同的函数名,彼此间通过形参列表加以区分。

    举例:

    函数名都为distance,但形参列表的个数不同;

    #include <iostream>
    #include <cmath>
    using namesapce std;
    double distance(float,float);
    double distance(float,float,float,float);
    int main()
    {
    	float point1X,point1Y,point2X,point2Y;
    	point1X=5.0;
    	point1Y=5.0;
    	point2X=7.0;
    	point2Y=7.0;
    	cout<<"The distance to the origin is:\n";
    	cout<<"Point 1 ("<<point1X<<","<<point1Y<<"): "<<distance(point1X,point1Y)<<endl;
    	cout<<"Point 2 ("<<point2X<<","<<point2Y<<"): "<<distance(point2X,point2Y)<<endl;
    	cout<<"The distance between two points ("<<point1X<<","<<point1Y<<") and ("
    		<<point2X<<","<<point2Y<<") is "<<distance(point1X,point1Y,point2X,point2Y)<<endl;
    	return 0;
    }
    double distance(float pX,float pY)
    {
    	return sqrt(pX*pX+pY*pY);
    }
    double distance(float p1X,float p1Y,float p2X,float p2Y)
    {
    	return sqrt((p2X-p1X)*(p2X-p1X)+(p2Y-p1Y)*(p2Y-p1Y));
    }

    函数名都为equal,但形参列表的数据类型不同;

    #include <iostream>
    using namesapce std;
    bool equal(int,int);
    bool equal(float,float);
    bool equal(char*,char*);
    
    int main()
    {
    	int i1,i2;
    	float f1,f2;
    	char *c1,*c2;
    	i1=3;i2=6;
    	f1=3.33333;f2=3.33332;
    	c1=new char[20];c2=new char[20];
    	strcpy(c1,"Hello");
    	strcpy(c2,"hello");
    	switch(equal(i1,i2))
    	{
    		case true:
    			cout<<i1<<"=="<<i2<<endl;break;
    		case false:
    			cout<<i1<<"!="<<i2<<endl;break;
    	}
    	switch(equal(f1,f2))
    	{
    		case true:
    			cout<<f1<<"=="<<f2<<endl;break;
    		case false:
    			cout<<f1<<"!="<<f2<<endl;break;
    	}
    	switch(equal(c1,c2))
    	{
    		case true:
    			cout<<c1<<"=="<<c2<<endl;break;
    		case false:
    			cout<<c1<<"!="<<c2<<endl;break;
    	}
    	delete[] c1;delete[] c2;
    	return 0;
    }
    bool equal(int a,int b)
    {
    	return a==b?true:false;
    }
    bool equal(float a,float b)
    {
    	if(fabs(a-b)<1e-6)
    		return true;
    	else 
    		return false;
    }
    bool equal(char* a,char* b)
    {
    	if(strcmp(a,b)==0)
    		return true;
    	else
    		return false;
    }

    类的构造函数是函数重载使用最普遍的地方;

    <pre name="code" class="cpp">#include <iostream>
    #include <string>
    using namesapce std;
    class ScoreRec{
    public:
    	ScoreRec()
    	{
    		name="";
    		ID="";
    		score=' ';
    	}
    	ScoreRec(string newName,string newID,char newScore)
    	{
    		name=newName;
    		ID=newID;
    		Score=newScore;
    	}
    	void getRecord(string& nameGet,string& IDGet,char& scoreGet)
    	{
    		nameGet=name;
    		IDGet=ID;
    		scoreGet=score;
    	}
    private:
    	string name;
    	string ID;
    	char score;
    };
    int main()
    {
    	ScoreRec A;
    	ScoreRec B("Henry","123456","B");
    	string name,id;
    	char score;
    	A.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	B.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	return 0;
    }


    二、为什么使用函数重载?

    在不同情形下,虽然处理数据的个数、数据类型可能不同,但用相同的函数名表达本质上相同的操作更符合人们的习惯,故函数重载给编程提供便利。


    三、使用函数重载时需要注意的问题

    1 若函数名、函数形参个数及对应的数据类型都相同,只是函数的返回类型不同,则不是有效的函数重载;编译时出现“函数定义重复”错误;


    2 若函数返回类型、函数名、形参个数及对应数据类型相同,只有参数传递方式不同,则不是有效的函数重载;


    3 调用重载函数时的二义性问题

    3.1 隐式类型转换引起的二义性问题

    在赋值、参数传递等情形下,当数据类型不匹配时,编译器会自动进行数据类型转换,即隐式类型转换;

    #include <iostream>
    #include <cmath>
    using namesapce std;
    bool equal(int,int);
    bool equal(double,double);
    int main()
    {
    	int i;
    	double d;
    	i=3;
    	d=3.0;
    	switch(equal(i,d))
    	{
    		case true:
    			cout<<i<<"=="<<d<<endl;
    			break;
    		case false:
    			cout<<i<<"!="<<d<<endl;
    			break;
    	}
    	return 0;
    }
    bool equal(int a,int b)
    {
    	return a==b?true:false;
    }
    bool equal(double a,double b)
    {
    	if(fabs(a-b)<1e-6)
    		return true;
    	else 
    		return false;
    }

    编译上述程序,出现以下错误:

    error C2666: 'equal' : 2 overloads have similar conversions.

    原因是可将equal(i,d)调用中的i转换成d型,也可将d转换成i型,编译器无法确定如何转换,因为两种转换都有相应的函数对应;

    3.2 使用默认参数时的二义性

    使用默认参数的函数本身就是将几个重载函数合而为一;

    #include <iostream>
    #include <cmath>
    using namesapce std;
    double distance(float,float);
    double distance(float,float,float x=0,float y=0);
    int main()
    {
    	float point1X,point1Y,point2X,point2Y;
    	point1X=5.0;
    	point1Y=5.0;
    	point2X=7.0;
    	point2Y=7.0;
    	cout<<"The distance to the origin is:\n";
    	cout<<"Point 1 ("<<point1X<<","<<point1Y<<"): "<<distance(point1X,point1Y)<<endl;
    	return 0;
    }
    double distance(float pX,float pY)
    {
    	return sqrt(pX*pX+pY*pY);
    }
    double distance(float p1X,float p1Y,float p2X,float p2Y)
    {
    	return sqrt((p2X-p1X)*(p2X-p1X)+(p2Y-p1Y)*(p2Y-p1Y));
    }

    编译上述程序,出现以下错误:

    error C2668: 'distance' : ambiguous call to overloaded function.

    原因是double distance(float p1X,float p1Y,float p2X=0,float p2Y=0)使用默认参数,它实际上是多个函数的集合体:

    若调用该函数时,给出4个实际参数,相当于调用double distance(float p1X,float p1Y,float p2X,float p2Y)函数;

    若调用该函数时,给出3个实际参数,相当于调用double distance(float p1X,float p1Y,float p2X)函数,同时将函数体中的p2Y设为0;

    若调用该函数时,给出2个实际参数,相当于调用double distance(float p1X,float p1Y)函数,同时将函数体中的p2X和p2Y设为0,实际上等价于求到原点的距离;

    在main函数中调用distance(point1X,point1Y)函数时,编译器无法确定调用的是哪个distance函数;

    注意:

    在指定默认参数时,必须从参数表的最右边开始并连续指定默认参数。



    • 复制构造函数


    一、复制构造函数对的语法形式

    复制构造函数的形参类型是类类型本身,且使用引用方式进行参数的传递。复制构造函数的原型形式如:X(const X&)

    其中,X是类名,形参通常指定为const;

    举例:

    class Computer{
    public:
    	Computer(const Computer& anotherComputer);
    };


    二、复制构造函数的使用场合

    若程序员没有为类定义复制构造函数,编译器会自动生成一个复制构造函数。

    1 用已有的对象对新对象进行初始化

    举例1:

    #include <iostream>
    #include <string>
    using namesapce std;
    class ScoreRec{
    public:
    	ScoreRec()
    	{
    		name="";
    		ID="";
    		score=' ';
    		cout<<"Default constructor.\n";
    	}
    	ScoreRec(string newName,string newID,char newScore)
    	{
    		name=newName;
    		ID=newID;
    		Score=newScore;
    		cout<<"Constructor with parameters.\n";
    	}
    	ScoreRec(const ScoreRec& anotherScoreRec)
    	{
    		name=anotherScoreRec.name;
    		ID=anotherScoreRec.ID;
    		score=anotherScoreRec.score;
    		cout<<"Copy constructor.\n";
    	}
    	void getRecord(string& nameGet,string& IDGet,char& scoreGet)
    	{
    		nameGet=name;
    		IDGet=ID;
    		scoreGet=score;
    	}
    private:
    	string name;
    	string ID;
    	char score;
    };
    int main()
    {
    	ScoreRec A;
    	ScoreRec B("Henry","123456","B");
    	ScoreRec C(B);
    	string name,id;
    	char score;
    	A.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	B.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	C.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	return 0;
    }

    上述程序的运行结果:

    Default constructor.

    Constructor with parameters.

    Copy constructor.




    Henry

    123456

    B

    Henry

    123456

    B

    分析:

    ScoreRec A;语句生成一个ScoreRec对象,每个对象的初始化由默认构造函数完成,则有Default constructor.输出;

    ScoreRec B("Henry","123456",'B');语句调用带参数的构造函数,则有Constructor with parameters.输出;该语句用参数对对象B的数据成员进行初始化;

    ScoreRec C(B);语句调用复制构造函数,用已有的对象B初始化新的对象C,则有Copy constructor.输出;用已有的对象初始化新对象时,新对象的数据成员的值和已有对象的数据成员的值完全相等;

    注意:

    编译器自动生成的复制构造函数的工作是将已有对象的内存空间中的数据按位复制一份到新对象的内存空间,称为浅复制。

    浅复制采用按位复制方法,当类的数据成员中有指针,会引发一些意想不到的问题。

    举例2:

    #include <iostream>
    #include <string>
    using namesapce std;
    class ScoreRec{
    public:
    	ScoreRec()
    	{
    		name=NULL;
    		ID=NULL;
    		score=' ';
    		cout<<"Default constructor.\n";
    	}
    	~ScoreRec()
    	{
    		if(name!=NULL)
    			delete[] name;
    		if(ID!=NULL)
    			delete[] ID;
    		cout<<"Deconstructor.\n";
    	}
    	ScoreRec(char* newName,char* newID,char newScore)
    	{
    		name=new char[strlen(newName)+1];
    		strcpy(name,newName);
    		ID=new char[strlen(newID)+1];
    		strcpy(ID,newID);
    		Score=newScore;
    		cout<<"Constructor with parameters.\n";
    	}
    	ScoreRec(const ScoreRec& anotherScoreRec)
    	{
    		name=new char[strlen(anotherScoreRec.name)+1];
    		ID=new char[strlen(anotherScoreRec.ID)+1];
    		strcpy(name,anotherScoreRec.name);
    		strcpy(ID,anotherScoreRec.ID);
    		score=anotherScoreRec.score;
    		cout<<"Copy constructor.\n";
    	}
    	void getRecord(string& nameGet,string& IDGet,char& scoreGet)
    	{
    		strcpy(nameGet,name);
    		strcpy(IDGet,Id);
    		scoreGet=score;
    	}
    private:
    	char* name;
    	char* ID;
    	char score;
    };
    int main()
    {
    	ScoreRec B("Henry","123456","B");
    	ScoreRec C(B);
    	char name[50],id[50];
    	char score;
    	B.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	C.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	return 0;
    }

    上述程序的运行结果:

    Constructor with parameters.

    Copy constructor.

    Henry

    123456

    B

    Henry

    123456

    B

    Deconstructor.

    Deconstructor.

    分析:

    由于ScoreRec类的数据成员使用指针类型,故特别留意内存空间的申请与释放!

    在上述程序中,内存空间的申请放在构造函数中,空间的释放放在析构函数中;构造函数在创建类对象时调用,析构函数在类对象的生命周期结束时调用;

    如下图为复制构造函数执行后两个ScoreRec类对象的内存情况,B和C中name的值不同,ID的值也不同。由于name和ID是指针数据类型,可理解为B和C中,name指向不同的内存空间,ID指向不同的内存空间;但内存空间中存储的是相同的内容,称为深复制,将一个对象中指针成员所指的内存空间的内容复制到另一个对象对应指针成员所指的内存空间中。


    若没有定义复制构造函数,编译器会自动生成一个复制构造函数。编译器自动生成的复制构造函数通过浅复制将已有对象中指针成员所指向的内存空间中的数据按位复制一份到新对象的对应指针成员所指向的内存空间。如下图为浅复制构造函数执行后两个ScoreRec类对象的内存情况。由于name和ID是指针类型数据,可理解为按位复制后,B和C中name指向相同的内存空间,ID也指向相同的内存空间。


    当对象的生命期结束时,系统会自动调用解析函数。同一内存的重复释放会导致运行时错误。这一点在类的数据成员中包含有指针时,需要特别注意!因此,需要程序员自己定义复制构造函数,通过深复制解决多个指针指向同一内存的问题。深复制时,B和C的name值是分别通过new操作赋值的,指向不同内存空间,不会出现同一内存空间的重复释放。


    2 调用以值形参传递类对象的函数

    举例:

    #include <iostream>
    #include <string>
    using namesapce std;
    class ScoreRec{
    public:
    	ScoreRec()
    	{
    		name=NULL;
    		ID=NULL;
    		score=' ';
    		cout<<"Default constructor.\n";
    	}
    	~ScoreRec()
    	{
    		if(name!=NULL)
    			delete[] name;
    		if(ID!=NULL)
    			delete[] ID;
    		cout<<"Deconstructor.\n";
    	}
    	ScoreRec(char* newName,char* newID,char newScore)
    	{
    		name=new char[strlen(newName)+1];
    		strcpy(name,newName);
    		ID=new char[strlen(newID)+1];
    		strcpy(ID,newID);
    		Score=newScore;
    		cout<<"Constructor with parameters.\n";
    	}
    	ScoreRec(const ScoreRec& anotherScoreRec)
    	{
    		name=new char[strlen(anotherScoreRec.name)+1];
    		ID=new char[strlen(anotherScoreRec.ID)+1];
    		strcpy(name,anotherScoreRec.name);
    		strcpy(ID,anotherScoreRec.ID);
    		score=anotherScoreRec.score;
    		cout<<"Copy constructor.\n";
    	}
    	void getRecord(string& nameGet,string& IDGet,char& scoreGet)
    	{
    		strcpy(nameGet,name);
    		strcpy(IDGet,Id);
    		scoreGet=score;
    	}
    private:
    	char* name;
    	char* ID;
    	char score;
    };
    void writeScoreRec(ScoreRec rec)
    {
    	char name[50],id[50];
    	char score;
    	rec.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	return;
    }
    int main()
    {
    	ScoreRec B("Henry","123456","B");
    	ScoreRec C(B);
    	writeScoreRec(B);
    	writeScoreRec(C);
    	return 0;
    }

    上述程序的运行结果:

    Constructor with parameters.

    Copy constructor.

    Copy constructor.

    Henry

    123456

    B

    Deconstructor.

    Copy constructor.

    Henry

    123456

    B

    Deconstructor.

    Deconstructor.

    Deconstructor.

    分析:

    writeScoreRec的形参rec相当于局部对象,将实参B传递给形参rec,相当于用已有对象初始化新对象,需要调用复制构造函数,因此输出Copy constructor.;

    当函数writeScoreRec返回时,局部对象rec的生命周期结束,需要调用析构函数,因此输出Deconstructor.;

    向值形参传递实参时,需生成一个实参的副本。当函数以值传递的方式进行类对象的传递时,会导致复制构造函数的调用,其语义相对复杂,因此在以类对象作为参数时,应使用引用形参的方式定义类对象的传递。


    3 调用返回值为类对象的函数

    增加函数setScoreRec,用以生成一个ScoreRec对象,根据参数设置该对象的属性数据,并返回该对象。

    举例:

    #include <iostream>
    #include <string>
    using namesapce std;
    class ScoreRec{
    public:
    	ScoreRec()
    	{
    		name=NULL;
    		ID=NULL;
    		score=' ';
    		cout<<"Default constructor.\n";
    	}
    	~ScoreRec()
    	{
    		if(name!=NULL)
    			delete[] name;
    		if(ID!=NULL)
    			delete[] ID;
    		cout<<"Deconstructor.\n";
    	}
    	ScoreRec(char* newName,char* newID,char newScore)
    	{
    		name=new char[strlen(newName)+1];
    		strcpy(name,newName);
    		ID=new char[strlen(newID)+1];
    		strcpy(ID,newID);
    		Score=newScore;
    		cout<<"Constructor with parameters.\n";
    	}
    	ScoreRec(const ScoreRec& anotherScoreRec)
    	{
    		name=new char[strlen(anotherScoreRec.name)+1];
    		ID=new char[strlen(anotherScoreRec.ID)+1];
    		strcpy(name,anotherScoreRec.name);
    		strcpy(ID,anotherScoreRec.ID);
    		score=anotherScoreRec.score;
    		cout<<"Copy constructor.\n";
    	}
    	void getRecord(string& nameGet,string& IDGet,char& scoreGet)
    	{
    		strcpy(nameGet,name);
    		strcpy(IDGet,Id);
    		scoreGet=score;
    	}
    	void setName(char* newName)
    	{
    		if(name!=NULL)
    			delete[] name;
    		name=new char[strlen(newName)+1];
    		strcpy(name,newName);
    	}
    	void setID(char* newID)
    	{
    		if(ID!=NULL)
    			delete[] ID;
    		ID=new char[strlen(newID)+1];
    		strcpy(ID,newID);
    	}
    	void setScore(char newScore)
    	{
    		score=newScore;
    	}
    private:
    	char* name;
    	char* ID;
    	char score;
    };
    ScoreRec setScoreRec(char* newName, char* newID, char newScore)
    {
    	ScoreRec tempRec;
    	tempRec.setName(newName);
    	tempRec.setID(newID);
    	tempRec.setScore(newScore);
    	return tempRec;
    }
    void writeScoreRec(ScoreRec rec)
    {
    	char name[50],id[50];
    	char score;
    	rec.getRecord(name,id,score);
    	cout<<name<<endl<<id<<endl<<score<<endl;
    	return;
    }
    int main()
    {
    	char stuName[50]="Henry";
    	char stuID[50]="123456";
    	char stuScore='B';
    	writeScoreRec(setScoreRec(stuName,stuID,stuScore));
    	return 0;
    }

    上述程序的运行结果:

    Default constructor.

    Copy constructor.

    Deconstructor.

    Henry

    123456

    B

    Deconstructor.

    分析:

    setScoreRec函数中有ScoreRec tempRec;语句,调用默认构造函数,输出Default constructor.

    setScoreRec函数中将tempRec的各项数据进行设置,再return tempRec;语句。当以值方式返回类对象时,系统实际创建一个临时的全局类对象(假设为tempObj),作为待返回对象的副本,并调用复制构造函数初始化tempObj对象。return tempRec;语句会导致一个tempObj的创建即复制构造函数的调用;同时由于setScoreRec函数返回,tempRec生命期结束,需要调用析构函数。

    最后一个Deconstructor.用于撤销tempObj。




    • 操作符重载


    操作符重载是指同一个操作符表示不同的实际操作;

    一、C++操作符的函数特性

    若将C++的操作符看作函数,操作数看作函数的参数,则一元操作符有一个参数,二元操作符有两个参数;

    如加法操作符是二元操作符,a+b可写成函数的调用方式+ (a, b);

    操作符具有函数的特性,也可像函数一样进行重载;操作符的重载可通过类的成员函数实现,也可通过友元函数实现;


    二、操作符重载的规则

    1 不是所有的操作符都可重载

    常见的不能重载的操作符包括:

    ::  .  .*  ?:      sizeof


    2 实现操作符重载的方法

    可用成员函数对操作符进行重载,也可用友元函数对操作符进行重载;

    操作符重载后对从操作符做出解释,但原有的基本语义,包括操作符的优先级、结核性和所需要的操作数个数均保持不变;

    注意:=、[]、()、->等操作符只能使用类成员函数进行重载!


    三、类成员函数操作符重载

    1 语法及实例

    操作符是一种特殊的成员函数,其语法形式为:

    <em>返回值类型 类名</em>:: operator <em>操作符 </em>(<em>形参列表</em>)
    {
    	<em>函数体代码</em>
    }

    举例:

    #include <iostream>
    #include <ctime>
    using namespace std;
    class StopWatch{      // 秒表类
    public:
    	StopWatch();
    	void setTime(int newMin, int newSec);
    	StopWatch operator - (StopWatch&);
    	void showTime();
    private:
    	int min;
    	int sec;
    };
    StopWatch::StopWatch()
    {
    	min = 0;
    	sec = 0;
    }
    void StopWatch::setTime(int newMin, int newSec)
    {
    	min = newMin;
    	sec = newSec;
    }
    StopWatch StopWatch::operator - (StopWatch& anotherTime)
    {
    	StopWatch tempTime;
    	int seconds;
    	seconds = min * 60 + sec - (anotherTime.min * 60 + anotherTime.sec);
    	if (seconds < 0)
    	{
    		seconds = -seconds;
    	}
    	tempTime.min = seconds / 60;
    	tempTime.sec = seconds % 60;
    	return tempTime;
    }
    void StopWatch::showTime()
    {
    	if (min>0)
    		cout << min << " minutes " << sec << "seconds" << endl;
    	else
    		cout << sec << " seconds" << endl;
    }
    
    int main()
    {
    	StopWatch startTime, endTime, usedTime;
    	cout << "Press enter key to start.";
    	cin.get();
    	time_t curtime = time(0);
    	tm tim = *localtime(&curtime);
    	int min, sec;
    	min = tim.tm_min;
    	sec = tim.tm_sec;
    	startTime.setTime(min, sec);
    
    	cout << "Press enter key to start.";
    	cin.get();
    	curtime = time(0);
    	tim = *localtime(&curtime);
    	min = tim.tm_min;
    	sec = tim.tm_sec;
    	endTime.setTime(min, sec);
    
    	usedTime = endTime - startTime;
    	cout << "Used time: ";
    	usedTime.showTime();
    
    	system("pause");
    	return 0;
    }

    运行结果:

    Press enter key to start.<回车>

    Press enter key to start.<回车>

    Used time: 5 seconds

    分析:

    在上述StopWatch类中,我们重载了-操作符,实现了两个秒表时间之间的差操作;这里重载的是二元操作符;

    重载操作符的成员函数的函数头为:StopWatch StopWatch::operator - (StopWatch& anotherTime)

    其中包含以下信息:

    a 函数名是operator -,表示重载操作符-

    b StopWatch::表示重载该操作符的类是StopWatch类

    c 该函数的返回类型是StopWatch类

    d 该函数带一个参数,参数类型是StopWatch类,应以引用方式传递

    在main函数中usedTime=endTime-startTime;解释为usedTime=endTime.operator - (startTime);

    即调用endTime的operator -函数,参数是startTime,返回值赋值给usedTime;


    2 参数个数和操作数个数的关系

    用成员函数实现操作符重载,函数参数个数个操作符所带操作数个数之间有以下规则:

    2.1 一元操作符实现为不带参数的成员函数

    2.2 二元操作符实现为带一个参数的成员函数

    注意:假设我们把二元操作符的操作数分别称为左操作数和右操作数,用成员函数重载时,则函数的参数中只包含右操作数,左操作数是调用该函数的类对象,该类对象通过this指针隐含传递给操作符函数;


    3 典型的操作符重载:赋值操作符重载

    #include <iostream>
    #include <string>
    using namespace std;
    
    class ScoreRec{
    public:
    	ScoreRec()
    	{
    		name = NULL;
    		ID = NULL;
    		score = ' ';
    		cout << "Default constructor." << endl;
    	}
    	ScoreRec(char* newName, char* newID, char newScore)
    	{
    		name = new char[strlen(newName) + 1];
    		strcpy(name, newName);
    		ID = new char[strlen(newID) + 1];
    		strcpy(ID, newID);
    		score = newScore;
    		cout << "Constructor with parameters." << endl;
    	}
    	~ScoreRec()
    	{
    		if (name!=NULL)
    			delete[] name;
    		if(ID!=NULL)
    			delete[] ID;
    		cout << "Deconstructor." << endl;
    	}
    	ScoreRec(ScoreRec& anotherScoreRec)
    	{
    		name = new char[strlen(anotherScoreRec.name) + 1];
    		strcpy(name, anotherScoreRec.name);
    		ID = new char[strlen(anotherScoreRec.ID) + 1];
    		strcpy(ID, anotherScoreRec.ID);
    		score = anotherScoreRec.score;
    		cout << "Copy constructor." << endl;
    	}
    	void getScoreRec(char* nameGet, char* IDGet, char& scoreGet)
    	{
    		strcpy(nameGet, name);
    		strcpy(IDGet, ID);
    		scoreGet = score;
    	}
    	void setName(char* newName)
    	{
    		if (name != NULL)
    			delete[] name;
    		name = new char[strlen(newName) + 1];
    		strcpy(name, newName);
    	}
    	void setID(char* newID)
    	{
    		if (ID != NULL)
    			delete[] ID;
    		ID = new char[strlen(newID)+1];
    		strcpy(ID, newID);
    	}
    	void setScore(char newScore)
    	{
    		score = newScore;
    	}
    	ScoreRec& operator = (ScoreRec& anotherScoreRec)
    	{
    		if (name != NULL)
    			delete[] name;
    		if (ID != NULL)
    			delete[] ID;
    		name = new char[strlen(anotherScoreRec.name)+1];
    		strcpy(name, anotherScoreRec.name);
    		ID = new char[strlen(anotherScoreRec.ID)+1];
    		strcpy(ID, anotherScoreRec.ID);
    		score = anotherScoreRec.score;
    		return *this;
    	}
    private:
    	char* name;
    	char* ID;
    	char score;
    };
    ScoreRec setScoreRec(char* newName, char* newID, char newScore)
    {
    	ScoreRec tempRec;
    	tempRec.setName(newName);
    	tempRec.setID(newID);
    	tempRec.setScore(newScore);
    	return tempRec;
    }
    void writeScoreRec(ScoreRec rec)
    {
    	char name[50], id[50];
    	char score;
    	rec.getScoreRec(name, id, score);
    	cout << name << endl << id << endl << score << endl;
    	return;
    }
    int main()
    {
    	char stuName[50] = "Henry";
    	char stuID[50] = "123456";
    	char stuScore = 'A';
    
    	ScoreRec tempRec;
    	tempRec = setScoreRec(stuName, stuID, stuScore);
    	writeScoreRec(tempRec);
    
    	system("pause");
    	return 0;
    }

    默认赋值操作符采用浅复制,即按位复制,当复制内容中存在指针变量时,需要定义自己的赋值操作符进行深复制;


    四、友元操作符重载

    1 友元的概念

    对于普通的类外函数f,要访问一个类A的私有成员和受保护成员是不可能的,除非将A的私有成员和受保护成员的访问权限改为public,但这样失去了类的封装作用,任何类外函数都可毫无拘束地使用其成员;但有时一个类外函数确实需要访问类的private或protected成员,因此在开放和封装之间折中,C++利用friend修饰符,设定类的友元,友元可对私有和受保护的成员进行操作。

    友元使类外函数能直接访问类的私有和受保护数据,提供了程序设计时的灵活性,但同时也破坏了累的封装特性,因此使用友元要慎重!

    类的友元可以使一个普通函数(非成员函数)、另一个类的成员函数、另一个完整的类;类的友元在类定义中使用friend保留字进行说明,在friend保留字后列出友元的名字(若友元为函数,则给出函数原型;若友元为类,则给出class类名);将另一个完整的类设为友元时,该类中所有成员函数都视为本类的友元函数。

    举例:

    #include <iostream>
    #include <string>
    using namespace std;
    
    class House{
    public:
    	House(string name,string address)
    	{
    		House::name = name;
    		House::address = address;
    	}
    	friend void showHouse(House& newHouse);
    private:
    	string name;
    	string address;
    };
    void showHouse(House& newHouse)
    {
    	cout << newHouse.name << endl;
    	cout << newHouse.address << endl;
    }
    int main()
    {
    	House clientHouse("YuQian", "ZhongShan Road");
    	showHouse(clientHouse);
    
    	system("pause");
    	return 0;
    }

    类House的定义中包含友元函数的声明:friend后紧跟函数showHouse的原型;

    注意:showHouse并不是类House的成员函数,因此在函数定义时不能写成:

    void House::showHouse(House& newHouse);

    但该函数可访问类House的私有成员name和address。


    2 友元操作符重载

    友元的另一个作用是操作符重载;

    举例:

    定义描述二维坐标(x,y)的类Pos,用友元函数实现+操作符的重载;

    #include <iostream>
    using namespace std;
    
    class Pos{
    public:
    	Pos(float newPos_x = 0, float newPos_y = 0)
    	{
    		pos_x = newPos_x;
    		pos_y = newPos_y;
    	}
    	void showPos()
    	{
    		cout << "x="<<pos_x << "\ty=" << pos_y << endl;
    	}
    	friend Pos operator + (Pos&, Pos&);
    	// 重载+操作符的友元函数的声明
    private:
    	float pos_x;
    	float pos_y;
    };
    Pos operator + (Pos& pos1, Pos& pos2)
    // 友元函数的定义
    {
    	Pos temp;
    	temp.pos_x = pos1.pos_x + pos2.pos_x;
    	temp.pos_y = pos1.pos_y + pos2.pos_y;
    	return temp;
    }
    
    int main()
    {
    	Pos pos1(25, 50), pos2(1, 2);
    	Pos pos3;
    	cout << "Pos 1:" << endl;
    	pos1.showPos();
    	cout << "Pos 2:" << endl;
    	pos2.showPos();
    	pos3 = pos1 + pos2;
    	cout << "Pos 1 + Pos 2:" << endl;
    	pos3.showPos();
    
    	system("pause");
    	return 0;
    }

    分析:

    程序中定义的类Pos描述一个二维坐标点,用友元函数重载+操作符,实现两个坐标点想家的操作:对两个坐标对象执行+运算,将两个坐标点的两个分量分别相加;

    在主函数中,生命了三个坐标对象,pos_1, pos_2, pos_3,其中pos_1+pos_2被解释为对+操作符重载函数的调用;

    上述操作符重载也可用成员函数来实现:

    #include <iostream>
    using namespace std;
    
    class Pos{
    public:
    	Pos(float newPos_x = 0, float newPos_y = 0)
    	{
    		pos_x = newPos_x;
    		pos_y = newPos_y;
    	}
    	void showPos()
    	{
    		cout << "x="<<pos_x << "\ty=" << pos_y << endl;
    	}
    	Pos operator + (Pos&);
    private:
    	float pos_x;
    	float pos_y;
    };
    Pos Pos::operator + (Pos& anotherPos)
    {
    	Pos temp;
    	temp.pos_x = pos_x + anotherPos.pos_x;
    	temp.pos_y = pos_y + anotherPos.pos_y;
    	return temp;
    }
    
    int main()
    {
    	Pos pos1(25, 50), pos2(1, 2);
    	Pos pos3;
    	cout << "Pos 1:" << endl;
    	pos1.showPos();
    	cout << "Pos 2:" << endl;
    	pos2.showPos();
    	pos3 = pos1 + pos2;
    	cout << "Pos 1 + Pos 2:" << endl;
    	pos3.showPos();
    
    	system("pause");
    	return 0;
    }

    注意:友元操作符重载和成员函数操作符重载的不同在于:

    a 一元操作符可实现不带参数的成员函数或带一个参数的友元函数;

    b 二元操作符可实现带一个参数的成员函数或带两个参数的友元函数;



    展开全文
  • 错误描述: 一些程序在 VC6 下运行好好地,但是放到 VC2008 及更高版本 VC 下编译却报错误以下仅以 VC2008 举例,高版本 VC 类似),例如使用如下语句: outtextxy(10, 20, "Hello World"); 在 VC6 下可以...

    错误描述:

    一些程序在 VC6 下运行好好地,但是放到 VC2008 及更高版本 VC 下编译却报错误(以下仅以 VC2008 举例,高版本 VC 类似),例如使用如下语句:

    outtextxy(10, 20, "Hello World");

     

    在 VC6 下可以成功编译,但在 VC2008 下编译后会有错误。

    错误提示如下:

    error C2665: “outtextxy”: 2 个重载中没有一个可以转换所有参数类型

     

    同样的,对于其他一些包含字符串调用的函数,例如 loadimage、drawtext 等,也会遇到类似问题。

    错误原因:

    简单来说,这是由于字符编码问题引起的。

    VC6 默认使用的 MBCS 编码,而 VC2008 及高版本 VC 默认使用的 Unicode 编码。以下详细解释这个问题:

    用 char 表示字符时,英文占用一个字节,中文占用两个字节。这样有一个严重的问题:两个连续字节,究竟是两个英文字符,还是一个中文字符?为了解决这个问题,Unicode 编码诞生了。Unicode 编码不管中文英文都用两个字节表示。

    对于 MBCS 编码,字符变量用 char 定义。
    对于 Unicode 编码中,字符变量用 wchar_t 定义。

    为了提高代码的自适应性,微软在 tchar.h 里面定义了 TCHAR,而 TCHAR 会根据项目定义的编码,自动展开为 char 或 wchar_t。

    在 Windows API 和 EasyX 里面的大多数字符串指针都用的 LPCTSTR 或 LPTSTR 类型,LPCTSTR / LPTSTR 就是“Long Point (Const) Tchar STRing”的缩写。所以可以认为,LPCTSTR 就是 const TCHAR *,LPTSTR 就是 TCHAR * 。

    于是,在 VS2008 里面,给函数传递 char 字符串时,就会提示前述错误。

    解决方案:

    解决方法有多个,目的一样,都是让字符编码相匹配。

    方法一:将所有字符串都修改为 TCHAR 版本。

    简单来说需要注意以下几点:

    1. 在程序中使用 #include 添加对 TCHAR 的支持。

    2. 对于字符串,例如 "abc" 用 _T("abc") 表示。就是加上 _T("")。

    3. 定义字符变量时,将 char 换成 TCHAR。

    4. 操作字符串的函数也要换成相应的 TCHAR 版本,例如 strcpy 要换成 _tcscpy。(详见 MSDN)

    方法二:在代码中取消 Unicode 编码的宏定义,让后续编译都以 MBCS 编码进行。

    方法很简单,只需要在代码文件的顶部增加以下代码:

    #undef UNICODE
    #undef _UNICODE

     

    这样就可以取消 Unicode 编码的宏定义,让整个项目以 MBCS 编码编译。

    方法三:在 VC2008 里面,将项目属性中的字符编码修改为 MBCS。

    操作步骤:点菜单“项目-> xxx 属性...”,点左侧的“配置属性”,在右侧的设置中找到“字符集”,修改默认的“使用 Unicode 字符集”为“使用多字节字符集”。

    设置完毕后,再次编译就可以看到问题已经解决。

    展开全文
  • 下面有关继承、多态、组合的描述,说法错误的是?A、封装,把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,不可信的进行信息隐藏 B、继承可以使用现有类的所有功能,并在无需...

    下面有关继承、多态、组合的描述,说法错误的是?

    A、封装,把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
    B、继承可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展
    C、隐藏是指派生类中的函数把基类中相同名字的函数屏蔽掉了
    D、覆盖是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同

    答案: BC

    解析:
    A:可以把成员设为private,外部的函数不可访问,设为public,外部的函数可以访问

    B:基类的私有成员不可以继承

    隐藏、覆盖和重载的区别:

    1、重载:是函数名相同,函数的参数个数、参数的类型
    参数的顺序不同,但是无法通过函数返回值的不同来进行重载,要在同一个类中

    2、覆盖:函数名相同,参数列表必须相同,必须位于基类和派生类中,在基类里的函数为虚函数

    3、隐藏:分两种情况
    1)函数名相同,函数的参数个数,参数的顺序,参数的类型不同,分别位于基类和派生类;(与重载的区别就是位于不同的两个类中)
    2)函数名相同,函数的参数相同,位于基类和派生类中,但是基类的函数不是虚函数;(与覆盖的区别在于:基类函数是否为虚函数)

    展开全文
  • 函数重载的重要性不言而明,但是你知道C++中函数重载是如何实现的呢(虽然本文谈的是C++中函数重载的实现,但我想其它语言也是类似的)?这个可以分解为下面两个问题 1、声明/定义重载函数时,是如何解决命名冲突的...
  • Kotlin系列之消除函数重载

    千次阅读 2018-03-09 22:08:55
    标签: kotlin &amp;nbsp;&amp;nbsp;&amp;nbsp;&... 消除函数重载 ...一、函数重载的带来问题 ...四、@JvmOverloads注解解决Java调用Kotlin重载函数问题 ... 无论是在Java或者C++中都有函数重载一说...
  • 重载和重写详解

    2020-07-07 15:27:38
    重载一般体现在构造方法,但不只是构造方法才能重载,Java允许任何方法重载,因此,要完整的描述一个方法,就要指出这个方法的方法名,以及参数类型.这个被称为方法签名 如String类的重载 返回类型和访问修饰符不是方法...
  • 总结----重载

    2020-09-11 13:47:45
    看过很多遍重载重载重载重载重载重载,但是就是不会(不能用自己的语言去完美的描述)。这是一个很糟糕的地现象。 总结至关重要: 概念要清晰: 重载定义: 严格意义上来讲,重载应该叫重载函数,因为只有函数才...
  • 运算符重载 对于面向对象的程序设计来说,运算符重载可以完成两个对象之间的复杂操作,比如两个对象的加法、减法等。运算符重载的原理是:一个运算符只是一个具有特定意义的符号,只要我们告诉编译程序在什么情况下...
  • C++中 多态 重载 覆盖

    千次阅读 2015-05-11 16:01:48
    面向对象的三大特征: 1.封装:保证对象自身数据的完整性、安全性 2.继承:建立类之间的关系,实现代码复用、方便系统的扩展 ...3.多态:相同的方法调用可实现不同的实现方式。多态是指两个或多个属于...运算符重载
  • 1. 以下关于运算符重载说法正确的是________。A.所有运算符都可以重载B. C++利用运算符重载可以创建新的运算符C.根据需要,在重载时可以提高重载运算符的优先级D. 不能改变重载运算符的优先级和结合性分析:A选项...
  • python之运算符重载

    2018-12-15 11:18:06
    这篇文章总结了一些关于python中运算符重载的设计技巧,在自定义类或者开发自定义库中尤其常见。 第一部分:概览 ...
  • 1、描述 ​ 在面向对象的语言中,允许我们在同一个类中定义多个方法名相同、参数列表(参数类型,参数个数)不同的方法,这样的形式我们称为方法重载。调用时编译器会根据实际传入参数的形式,选择与其匹配的方法...
  • 重载(overload)和覆盖(override)是C++中关于函数的两个基础概念,但是如果让你说出他们具体的描述和区别,一下子还真是不太容易说的很清楚和全面,这里简单把记录一下,作为备忘。关于隐藏我觉得是个误解,C++中...
  • 赋值运算符重载和拷贝构造函数

    千次阅读 2017-08-02 12:13:32
    C++本质:类的赋值运算符=的重载,以及深拷贝和浅拷贝 关键词:构造函数,浅拷贝,深拷贝,堆栈(stack),堆heap,赋值运算符 摘要:  在面向对象程序设计中,对象间的相互拷贝和赋值是经常进行的操作。  如果对象...
  • 在下列运算符中,不能重载的是(B )。(A)! (B)sizeof (C)new (D)delete2.在下列关于运算符重载描述中,(D )是正确的。(A)可以改变参与运算的操作数个数 (B)可以改变运算符原...
  • 方法重写和方法重载的区别

    千次阅读 2018-01-17 15:19:02
    继承和多态都是面向对象...方法重写及方法重载在继承和多态性方面的应用中会存在很多问题,这些概念很容易混淆,掌握重写和重载的区别学会使用多态的方式编写程序、提高程序的可维护性奠定了基础。 一、方法重写(0
  • 虚函数和函数重载

    千次阅读 2016-05-12 11:44:17
    函数重载: 面向对象编程过程中,针对定义大量的函数,函数命名难度开始出现,为了解决此问题,希望通过函数重名来达到简化编程的目的。 例如,要声明两个求绝对值的函数:int abs(int); double abs(double);C++...
  • 重载两运算符格式如下: istream & operator >> (istream &, 自定义类 &); ostream & operator 并且只能将其重载为友元函数,而不能作为自定义类的成员函数, 原因如下: 定义为成员函数,那么就隐含this指针了,...
  • 重载OverLoad与重写Override的区别这也是一个在面试中经常被问到的问题,主要的区别就是以下几点: 重载OverLoad: (1) 方法名相同 (2) 参数个数不同 (3) 参数类型不同 (4) 参数列表顺序不同 (5) 在参数的个数、类型...
  • Effective-Java 明智审慎地使用重载

    千次阅读 2020-06-22 11:01:07
    52. 明智审慎地使用重载 下面的程序是一个善意的尝试,根据 Set、List 或其他类型的集合它进行分类: // Broken! - What does this program print? public class CollectionClassifier { public static String ...
  • C#中方法重载与方法重写区别

    千次阅读 2013-05-08 11:03:19
    方法重载:在一个类中存在方法名相同、参数列表不同(参数个数或者参数类型不同)、返回值类型可以不相同,调用的时候根据参数列表的不同来正确调用。 class Program  {  public static void Main(string[]...
  •  重载(overloaded)、内联(inline)、const 和virtual是C++独有而C不具有的四种机制。其中重载和内联机制既可用于全局函数也可用于类的成员函数,const 与virtual机制仅用于类的成员函数。重载和内联是一把双刃...
  • 监控sql语句的重载

    千次阅读 2009-07-17 15:01:00
    二, 监控sql语句的重载率Sql的重载率, 就是相同的语句, 由于无法使用共享池里已经保存的执行计划而不得不重新将代码载入后执行分析,建立查询树后再进行执行的一个过程. 极端糟糕的情况下, 重载率可能接近于1 , 就是...
  • 真正零停机 HAProxy 重载

    千次阅读 2016-12-05 21:01:57
    使用plug qdisc和以下标准Linux技术, 我们可以实现HAProxy重载零宕机: tc :Linux流量控制。这使我们能够建立基于过滤器路由连接的排队规则。在最新的Linux版本上自带libnlutils,它提供了一些较新的...
  • 方法重载 1、方法重载又被称为: overload 2、什么时候考虑使用方法重载? 功能相似的时候,尽可能让方法名相同。 [但是:功能不同/不相似的时候,尽可能让方法名不同。] 3、什么条件满足之后构成了方法重载? ★...
  • 成员函数的重载、覆盖(override)与隐藏很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防。 重载与覆盖 成员函数被重载的特征: (1)相同的范围(在同一个类中); (2)函数...
  • 覆盖(不是重载)了equals方法,请一定要覆盖hashCode方法 为了能让集合框架中的类如HashMap正常工作,必须保证同时覆盖equals()和hashCode(),而且注意不要由于写错了参数类型,而重载了这两个方法,却并...
  • 博客已经迁移到这里啦Item 26已经解释了,不管是全局函数还是成员函数(尤其是构造函数)而言,universal引用的重载会导致一系列的问题。到目前为止,我也已经给出了好几个例子,如果它能表现得和我们期待的一样...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,800
精华内容 21,920
关键字:

以下对重载描述错误的是