技术标签: C++
运算符的重载
所谓重载,就是重新赋予新的定义,也就是一名多用;
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i) { }
Complex Complex_Add(Complex &);
void Display();
private:
double real;
double imag;
};
Complex Complex :: Complex_Add(Complex &c2)
{
Complex temp;
temp.real = this->real + c2.real;
temp.imag = this->imag + c2.imag;
return temp;
}
void Complex :: Display()
{
cout << "The complex is : " << real << " + " << imag << "i" << endl;
}
int main()
{
Complex c1(3,4),c2(1.2,-4),c3;
c3 = c1.Complex_Add(c2);
cout << "c1 = " ; c1.Display();
cout << "c2 = " ; c2.Display();
cout << "c1 + c2 = " ; c3.Display();
return 0;
}
这种调用方式太过繁琐,所以能不能用 + 运算符来实现两个复数的相加呢?
运算符重载的方法:
运算符重载的方法是 : 定义一个重载运算符的函数;
在需要执行被重载的运算符时,系统就自动调用该函数,以完成运算;
运算符重载,实质上就是函数的重载!!
一般格式:
函数类型 operator 运算符名称(形参列表)
{
对运算符的重载处理;
}
例:对 + 进行复数加法的运算符重载:
Complex operator + (Complex &c1, Complex &c2);
其中:
1. operator 是关键字,专门用于定义重载运算符函数的;
2. 运算符名称就是 C++ 提供给用户的预定义运算符;
注:
3. 函数名是由 operator和运算符组成: 即 operator + 为函数名 !!!
4. 两个形参是 Complex 类对象的引用,要求两个实参是 Complex 类型!!
在定义了重载运算符之后,即: 函数 operator+ 重载了运算符 + ;
此后,在执行 c1 + c2 的表达式的时候:
系统就会调用 operator+ 函数,把 c1,c2 作为实参,与形参进行虚
实结合;
例:
两个整数相加 :
int operator+ (int a, int b)
{
return (a+b);
}
此时如果有表达式 5+8 ,就调用这个函数,将 5和8作为实参,函数的
返回值为13;
例:利用重载运算符实现两个复数相加:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i) { }
Complex operator+ (Complex &);
void Display();
private:
double real;
double imag;
};
Complex Complex :: operator+ (Complex &c2)
{
Complex temp;
temp.real = real + c2.real;
temp.imag = imag + c2.imag;
return temp;
}
void Complex :: Display()
{
cout << "The complex is : " << real << " + " << imag << "i" << endl;
}
int main()
{
Complex c1(3,4),c2(1.2,-4),c3;
c3 = c1 + c2;
cout << "c1 = " ; c1.Display();
cout << "c2 = " ; c2.Display();
cout << "c1 + c2 = " ; c3.Display();
return 0;
}
以 c2 为实参调用 c1 的运算重载函数 operator+(Complex &c2), 进行
求值;
重载函数的简便写法:
Complex Complex :: operator+ (Complex &c2)
{
return Complex(real + c2.real,imag + c2.imag);
}
其中的 Complex(xx,xx) 是建立一个临时变量,是一个无名对象,在建
立时使用构造函数;
说明:
在运算符被重载之后,其原有的功能还是被保留,同时又增加了别的功能;
编译系统通过对上下文( 运算符的两侧,单目运算符为一侧 ) 数据类型进
行判断。
C++ 提供的运算符重载使得用户可以自己编写对 对象进行的操作;
重载运算符的规则:
1. 只能对 C++ 中已有的运算符进行重载;
2. 五个不能重载的运算符:
. 成员访问
.* 成员指针访问
:: 域运算符
sizeof 长度运算符
?: 条件运算符
前两个不能重载是为了保证访问成员的功能不被改变;
域运算符和sizeof 运算符的运算对象是类型,而不是变量或一般表达式,不具备重载功能;
3. 重载不能改变运算符运算对象的个数:
即 双目运算符需要两个参数,单目运算符需要一个参数。。
4. 重载不能改变运算符的优先级别;
5. 重载不能改变运算符的结合性:
如:赋值运算符是右向左,重载后不变;
6. 重载运算符函数不能带有默认参数!
7. 重载运算符必须和用户自定义类型的对象一起使用:
其参数至少有一个类对象(或类对象的引用)!!!
即: 不能全部是 C++ 的标准类型!!
例:
int operator+ (int a, int b)
{
return (a-b);
}
此时在计算时系统 对于加减将会不明确!
如果有两个参数,则既可以都是类对象,也可以一个是类对象,一个是C++
的标准类型:
例:
Complex operator+ (int a, Complex &c)
{
return Complex(a + c.real, c.imag);
}
即,一个整数和一个复数的加法运算;
8. 用于类对象的运算符一般需要重载,但是有两个例外:
运算符 = 和 & 可以不用重载,
1): 赋值运算符 = :
可以用于每一个类对象,可以直接用于同类对象之间相互赋值。
由于系统已经为用户的每一个新声明的类重载了一个运算符,作用是逐个复制类的成员。
但是有的时候,默认的对象赋值运算符不能满足程序的要求!!
例:数据成员中包括动态分配内存的指针成员时,在复制成员时可能会出现危险,此时要求用户自己编写。
2): 地址运算符 & :
返回类对象在内存中的起始地址;
9. 一般应当使重载运算符的功能类似于作用于标准类型数据时的功能,
防止可读性差。
10. 运算符重载函数,可以是普通函数,类的成员函数或者类的友元函数;
运算符重载函数作为类成员函数和友元函数
对于 + 双目运算符,为何在例子中只有一个参数:
实际上运算符重载函数有两个参数,但是由于重载函数是 Complex 类中的成员函数;
有一个参数是隐含的,运算符函数是通过 this 指针来访问的!!!
即:
重载函数访问了两个参数 : this->real , c2.real;
且
在将运算符函数重载为成员函数后,如果出现含该运算符的表达式,
c1 + c2 编译系统将改为 : c1.operator+ (c2)
即通过对象 c1 调用运算符重载函数,并以表达式中的第二个参数( c2 ) 作为实参。
重载函数除了作为成员函数外,还可以作为非成员函数:
例:作为 Complex 的友元函数:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i) {}
friend Complex operator + (Complex &c1, Complex &c2);
void Display();
private:
double real;
double imag;
};
Complex operator + (Complex &c1, Complex &c2)
{
return Complex(c1.real + c2.real,c1.imag + c2.imag);
}
void Complex :: Display()
{
cout << "The complex is : " << real << " + " << imag << "i" << endl;
}
int main()
{
Complex c1(3,4),c2(1.2,-4),c3;
c3 = c1 + c2;
cout << "c1 = " ; c1.Display();
cout << "c2 = " ; c2.Display();
cout << "c1 + c2 = " ; c3.Display();
return 0;
}
如:
将一个复数和一个整数相加, c1 + i ,如果将其作为成员函数有:
Complex Complex :: operator + (int &i)
{
return Complex(real + i,imag);
}
注意:
在表达式中的重载运算符 + 左侧必须为 Complex 类型的对象:
c3 = c2 + i; // 正确;
如果写为了:
c3 = i + c2; // 错误,左侧不是类对象 !!
2. 重载函数作为友元函数:
运算符左侧作为 C++ 标准类型或者是非 Complex 型的对象;
此时运算符重载函数不能作为成员函数,只能作为非成员函数;
并且如果此时函数需要访问类的私有成员,则必须声明为该类的友元函数!!
例: 在 Complex 中 声明重载运算符为该类的友元函数:
friend Complex operator + ( int &i, Complex &c );
同时在类外定义友元函数:
Complex operator + (int &i, Complex &c)
{
return Complex(i + c.real, c.imag);
}
注:
1. 将双目运算符作为友元函数时,函数的形参表列中必须有两个参数,不可省
略!!!
2. 形参的顺序任意,不要求第一个对象为类对象!
3. 但是在使用运算表达式中:
要求运算符左侧的操作数与函数的第一个参数对应;
运算符右侧的操作数与函数的第二个参数相对应。
如:
c3 = i + c2; // 正确,类型匹配;
c3 = c2 + i; // 错误,类型不匹配!!!
即在此处数学上的交换律并不适用,需要再一次重载运算符 + :
Complex operator + ( Complex &c ,int &i )
{
return Complex(i + c.real,c.imag);
}
此后,i + c2 与 c2 + i 都合法;
注: 不可把两个函数都作为成员函数;
3. C++ 规定,有的运算符必须定义为类的成员函数:
赋值运算符,下标运算符,函数调用运算符;
有的运算符不能定义为类的成员函数:
流插入 << , 流提取 >> , 类型转换运算符;
所以一般将:
单目运算符重载为成员函数,双目运算符重载为友元函数;
重载双目运算符:
双目运算符有两个操作数( 通常在操作符两侧 ),所以在重载函数中有两个参数:
例:
定义一个字符串类 string,用来存放不定长的字符串;
并且重载运算符 == ,> , <, 用于两个字符串的等于,大于,小于的比较运算。
class String
{
public:
String()
{
p = NULL;
}
String(char *str)
{
p = str;
}
void Display();
void Length();
friend bool operator > (String &str1, String &str2);
friend bool operator < (String &str1, String &str2);
friend bool operator == (String &str1, String &str2);
private:
char *p;
int length;
};
void String :: Display()
{
cout << p;
}
void String :: Length()
{
for(length = 0; *p != NULL; p++,length++)
{
;
}
cout << "The length of the string is : " << length << endl;
}
bool operator > (String &str1, String &str2)
{
if(strcmp(str1.p , str2.p) > 0)
{
return true;
}
else
{
return false;
}
}
bool operator < (String &str1, String &str2)
{
if(strcmp(str1.p , str2.p) < 0)
{
return true;
}
else
{
return false;
}
}
bool operator == (String &str1, String &str2)
{
if(strcmp( str1.p , str2.p ) == 0)
{
return true;
}
else
{
return false;
}
}
void Compare(String &str1, String &str2) // 增加一个compare 函数,减轻main函数的负担;
{
if( ( operator > (str1,str2) ) == 1 )
{
str1.Display();
cout << '>';
str2.Display();
}
else if( ( operator < (str1,str2) ) == 1 )
{
str1.Display();
cout << '<';
str2.Display();
}
else if( ( operator == (str1,str2) ) == 1 )
{
str1.Display();
cout << '=';
str2.Display();
}
}
int main()
{
String str1("BOOK!"),str2("BOOK!");
Compare(str1,str2);
return 0;
}
重载单目运算符:
单目运算符只有一个操作数,与双目运算符的重载类似:
只不过重载函数只有一个参数,且如果作为成员函数,还可以省略!
例:
自加 ++ 运算符的重载: 时钟的自加;
class Time
{
public:
Time()
{
minute = 0;
sec = 0;
}
Time(int m,int s) : minute(m),sec(s) {}
Time operator ++ ();
void Display();
private:
int minute;
int sec;
};
Time Time :: operator ++ ()
{
(this->sec)++;
if(sec >= 60)
{
sec = 0;
minute++;
}
return *this; // 返回的是一个指向正在操作的这个 Time类型的对象的指针!
}
void Time :: Display()
{
cout << this->minute << " : " << this->sec << endl;
}
int main()
{
Time time1(34,0);
for(int i = 0; i < 61; i++)
{
++time1; // 注: 此时必须写为 ++XXX, 即写在自加运算符后(右结合性)!!
time1.Display();
}
return 0;
}
例: 前置自加运算符 和 后置自加运算符:
class Time
{
public:
Time()
{
minute = 0;
sec = 0;
}
Time(int m,int s) : minute(m),sec(s) {}
Time operator ++();
Time operator ++(int);
void Display();
private:
int minute;
int sec;
};
Time Time :: operator ++ () // 前置自增运算符 ++time : 先自加再返回!
{
if( ++sec >= 60 ) // 此处不可写为 sec ++ !!!!;
{
sec -= 60;
minute++;
}
return *this;
}
Time Time :: operator ++ (int) // 后置自增运算符 time++ : 返回的是自加前的对象!!
{
Time temp(* this); // 把之前的 this 内容保存起来;
sec++;
if(sec >= 60)
{
sec -= 60;
++minute;
}
return temp; // 返回的是自加前的对象!
}
void Time :: Display()
{
cout << this->minute << " : " << this->sec << endl;
}
int main()
{
Time time1(34,59),time2;
cout << "time1 : ";
time1.Display();
++time1;
cout << "time1 : ";
time1.Display();
time2 = time1++; // 把自加前的 time1 赋值给 time2 ,然后 time1 自加;
cout << "time1 : ";
time1.Display();
cout << "time2 : ";
time2.Display();
return 0;
}
注:
1. 前置自增运算符 和 后置自增运算符的区别:
1) : 前置自增 ++time :
先自加,返回的是自加后的值;
2) : 后置自增 time++ :
先返回原值,之后再自加一!!
2. 如果不区分前置和后置,则使用operator++( )或operator--( )即可;
否则:要使用operator++( )或operator--( )来重载前置运算符!!
使用operator++(int) 或 operator--(int)来重载后置运算符,调用时,参数int被传递给值0。
重载流插入和流提取运算符:
C++ 中的流插入 << 和 流提取 >> 功能是在类库中提供的,即 istream 和 ostream;
在类库的头文件中已经包括了输出 标准类型的数据,但是对于用户自定义的类,需要自行定义:
重载的形式函数:
istream & operator >> ( istream & , 自定义类 & );
ostream & operator << ( ostream & , 自定义类 & );
即:
重载运算符 >> 函数的第一个函数和函数的类型都必须是 istream & 类型;
第二个参数是要进行输出操作的类。
所以!!! 只能把 << >> 的重载函数作为友元函数或者普通的函数,而不可作为成员函数!
1. 重载流插入运算符 << :
用插入运算符 << 来输出用户自己的类对象的信息。
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i) {}
Complex operator + (Complex &c2);
friend ostream& operator << ( ostream & , Complex & );
private:
double real;
double imag;
};
Complex Complex :: operator + (Complex &c2) // 重载 + 运算符!
{
return Complex(real + c2.real, imag + c2.imag);
}
ostream& operator << (ostream &output, Complex &c2) // 重载流插入 << 运算符
{
output << "(" << c2.real << " + " << c2.imag << "i)" << endl;
return output;
}
int main()
{
Complex c1(2,4),c2(6,10),c3;
c3 = c1 + c2;
cout << c3;
return 0;
}
1. 运算符重载中的 形参 output 是 ostream类对象的引用名, 是用户可以任意取
名的!
2. 编译系统把 cout << c3 解释为 :
operator << ( cout , c3 ); 所以调用用户自定义的重载函数!
3. return output 就是将输出流 cout 的现状返回,即保留输出流的现状:
例:
cout << c2 << c3;
则先处理 (cout << c2) << c3;
此后相当于 cout(新值) << c3; 此时 << 左侧依旧是 ostream类型,右侧还是
Complex 类型,还可继续调用;
所以 C++ 规定 << 重载时的第一个参数和函数的类型必须都是 ostream 类型的引
用:
就是为了返回 cout 的当前值以便于连续输出!
2. 重载流提取运算符 >> :
c++ 预定义的 >> 的作用是从一个输入流中提取数据;
而重载的目的是为了提取用户需要的自定义类型的对象的信息;
例:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i){ }
friend ostream & operator << ( ostream & , Complex & );
friend istream & operator >> ( istream & , Complex & );
private:
double real;
double imag;
};
ostream& operator << (ostream &output, Complex &c)
{
output << "(" << c.real << " + " << c.imag << "i)";
return output;
}
istream& operator >> (istream& input, Complex &c)
{
cout << "Input the part & the imaginary part of the complex number :";
input >> c.real >> c.imag;
return input;
}
int main()
{
Complex c1,c2;
cin >> c1 >> c2;
cout << " c1 = " << c1 << endl;
cout << " c2 = " << c2 << endl;
return 0;
}
1. 在执行 cin >> c1时,同样的调用, operator >> 函数,将 cin 和 c1 传递给形
参;
2. 函数返回新的 cin 值,即用 cin 可以连续的从输入流提取数据给 Complex 对
象!
注:3. 每遇到一个 >> 就调用一次函数!
例改:
当出现负数时,将出现 3 + -10i 的错误;需要改:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i){ }
friend ostream & operator << ( ostream & , Complex & );
friend istream & operator >> ( istream & , Complex & );
private:
double real;
double imag;
};
ostream& operator << (ostream &output, Complex &c)
{
output << "(" << c.real;
if(c.imag >= 0)
{
output << " + ";
}
output << c.imag << "i)" << endl;
return output;
}
istream& operator >> (istream& input, Complex &c)
{
cout << "Input the part & the imaginary part of the complex number :";
input >> c.real >> c.imag;
return input;
}
int main()
{
Complex c1,c2;
cin >> c1 >> c2;
cout << " c1 = " << c1 << endl;
cout << " c2 = " << c2 << endl;
return 0;
}
注:
利用引用类型作为函数的形参可以在调用函数的时候:
1. 不是用值传递的方式进行虚实结合,而是通过传地址的方式使形参成为别名;
因此不生成临时变量,减少了时间和空间上的开销!
2. 重载函数的引用对象如果是对象的引用时,返回的不是常量,而是引用所代表的
对象;
所以可以出现在赋值号的左侧,成为左值,可以被赋值或者进行其他操作 (连续
输入,输出等)
不同数据类型间的转换:
1. 标准类型数据间的转换:
1. 隐式类型转换:
C++ 根据运算式中的数据类型自动完成;
2. 显式类型转换:
数据类型名(数据) ;
例: int(89.4); 即把 89.4 转换为 89;
注: 在 C语言中为: (类型名)数据 ;
对于用户自己声明的类,需要定义专门的函数来处理数据类型转变!
1. 转换构造函数:
将一个其他类型的数据转化为一个指定的类的对象。
原来学过的: 1) : 初始化构造函数;Complex(double r,double i);
2) : 复制构造函数: Complex(Complex &c)
说明:
1. 转换构造函数只有一个参数:
例:
Complex(double r)
{
real = r;
imag = 0;
}
作用为 : 将 double 类型的参数 r 转化为 Complex 类的对象;(即 r为实部,虚部
为零);
2. 在类体中,可以有转换构造函数,也可以没有转换构造函数!!
3. 以上学过的几类构造函数可以同时出现在同一个类体之中,他们都是构造函数的
重载。
( 但是形式不同,编译系统会自动根据建立对象时所给实参来匹配! )
例:
构造了转换构造函数后:
1. Complex c1(3.5); // 建立了一个将 double 3.5 转化为 3.5 + 0i的虚数;
2. Complex c(3.6); // 建立了一个无名对象;
3. Complex c1(3.6);
c = c1 + 2.5; // 若未定义一个 Complex 和 一个 double 相加的重载则错误;
c = c1 + Complex(2.5); // 若定义了两个复数相加,正确!
4. 转换构造函数也是一种构造函数,一般都作为类型转换。
若不作为类型转换:
Complex(double r)
{
cout << r;
}
理论上是可行的,但是不具有实际意义!
注:5. 转换构造函数只能有一个参数!!!!
2. 类型转换函数:
利用构造转换函数可以将一个指定类型的数据转化为类的对象,但是不能反过来;
所以只能通过 c++ 提供的类型转换函数来解决:
即、将一个类的对象转换成另一类型的函数。
例:
在 Complex 类中声明:
operator double()
{
return real; // 函数返回虚数的实部;
}
注: 函数名为 operator double 。
一般形式为: operator 类型名() { ... }
1. 在函数名前不能指定函数类型,函数也没有参数!!
2. 返回值的类型是由函数名指定的类型名决定的;
3. 类型转换函数必须作为成员函数,因为转换的本体是本类的对象;不可作为普通或友元函数;
说明:
1. double 类型经过重载之后,除了原有的含义之外,还获得了新的含义( 把Complex 转化为 double型 )
对于编译系统,不仅能识别原有的 double 类型,还会把 Complex 类对象作为 double 类型处理!!
即: Complex 相当于具有了双重国籍,在不同的场合,以不同的面貌出现;
2. 转换构造函数 和 类型转换函数有一个共同的功能:
当需要的时候,编译系统可以自动调用这些函数,自动建立临时变量!
例:
若定义了 double型 : d1,d2 ; Complex 型: c1,c2;
则对于程序中的表达式:
d1 = d2 + c1;
编译系统发现 + 左端为 double ,右侧为 Complex ;
1): 首先:检查有无对 + 的运算符重载;
2): 若没有:检查有无类型转换函数:
发现 double 的重载函数后,调用该函数把 Complex 转化为 double 类型(建立一
个 double temp),
在和 c2 相加之和,把一个 double 类型的数据赋值给 d1;
对于表达式( 若定义了转换构造函数和 + 的重载,但是没有定义 double 的类型转换函数 ):
c2 = c1 + d2;
1):首先:检查发现 有 operator+ 函数,但是是 Complex 的友元函数( 要求两个 Complex )
在类中没有对 double 重载,所以不能把 c1 转化为 double 然后相加;
此时: 调用转换构造函数:Complex(&d2) ,产生temp 的 Complex 对象;
在调用 operator+ 函数,将两个复数相加 再赋值给 c2;
即: c2 = c1 + Complex(d2)
例:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r,double i) : real(r),imag(i){}
Complex(double r) // 定义转换构造函数,double 转化为 Complex;
{
real = r;
imag = 0;
}
operator double() // 定义类型转换函数,Complex 转化为 double;
{
return real;
}
private:
double real;
double imag;
};
int main()
{
Complex c1(3,4),c2(5,-10),c3;
double d;
d = 2.5 + c1; // double + Complex;
cout << d << endl;
return 0;
}
1. 由于已经定义了成员函数 operator double,就可以利用它将Complex对象转
化为double类型;
注: 程序中不必显示的调用类型函数,是自动被调用的(隐式调用);
即编译系统在处理表达式 2.5 + c1 的时候,发现运算符 + 左侧为 double 类型,
右侧为 Complex 类型,
而又无运算符 + 重载函数,不能直接相加;
但是有对 double 的重载函数,因此调用该函数,并返回double;
2. 如果改为:
d = c1 + c2; // d 为 double 类型;
将 c1 和 c2 两个类对象相加,得到一个临时的 Complex 对象,而又有对double类型的重载函数,
于是调用此函数,把临时类对象转换为 double 数据,然后复制给 d;
使用类型转换函数的好处:
例: 程序需要对一个 double 和 Complex类对象进行运算,如果不用类型转换
函数, 就要对多种运算符进行重载,以便能进行各种运算!
但是如果对 double 类型进行重载(使 Complex 类对象转化为 double 类型
数据),就不必对各种运算符进行重载了;
例:包含转换构造函数、运算符重构函数和类型转换函数的程序:
1. 只包含转换构造函数和运算符重构函数:
class Complex
{
public:
Complex() // 默认构造函数;
{
real = 0;
imag = 0;
}
Complex(double r) // 转换构造函数;
{
real = r;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i) {} // 初始化构造函数;
friend Complex operator + (Complex c1, Complex c2); // 重载运算符 + 的友元函数;
void Display();
private:
double real;
double imag;
};
Complex operator + (Complex c1, Complex c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
void Complex :: Display()
{
cout << "(" << real << " + " << imag << "i)" << endl;
}
int main()
{
Complex c1(3,4),c2(5,-10),c3;
c3 = c1 + 2.5;
c3.Display();
return 0;
}
在 Complex 中定义了转换构造函数,并规定了怎样构成一个复数,所以在处理 c1 +
2.5时:
编译系统解释为: operator + ( c1, 2.5 );
即:
operator + (c1, Complex(2.5)) 把 2.5 转化为临时变量;
注: 如果改为 2.5 + c1 程序也是可以运行的!!!!
所以在已经定义了转换构造函数的情况下,将运算符 + 重载为友元函数,在两个复数相加时,可用交换律;
注: 如果运算符 + 重载函数不作为 Complex 类的友元函数,而作为 Complex 类
的成员函数:
则不满足交换律:
对于表达式 2.5 + c1 ,
c++编译系统把它解释为 : (2.5).operator + (c1) 错误!
所以:
运算符函数重载为成员函数,他的第一个参数必须是本类的对象!!
当第一个操作数不是类对象时,不能将运算符函数重载为成员函数 !
否则交换律不适用!
即: 一般情况下,将:
双目运算符函数重载为友元函数。而单目运算符则重载为成员函数;
3. 如果一定要将运算符函数重载为成员函数,而第一个操作数又不是本类对象:
只能再重载一个运算符 + 函数,其第一个参数为 double 类型,且只能是友元函数。
函数原型: friend operator + (double , Complex &)
例:
2. 增加类型转换函数:
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r)
{
real = r;
imag = 0;
}
Complex(double r, double i) : real(r),imag(i) {}
operator double() // 类型转换函数;
{
return real;
}
friend Complex operator + (Complex c1, Complex c2);
void Display();
private:
double real;
double imag;
};
Complex operator + (Complex c1, Complex c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
void Complex :: Display()
{
cout << "(" << real << " + " << imag << "i)" << endl;
}
int main()
{
Complex c1(3,4),c2(5,-10),c3;
c3 = c1 + 2.5;
c3.Display();
return 0;
}
有时候我们需要自己整理sql脚本可以直接通过plsql直接导出,具体操作如下 导出表数据(插入sql语句)打开plsql客户端,选中工具(Tools)下的导出表(Export data) 选中你自己需要导出的表以及要导出的路径点击导出就可以了,这样便能导出表的数据导出建表语句打开plsql客户端,选中工具(tool)下的导出用户对象(Export user object) 选中你自己...
这段时间太多事,生信学习耽误了很长一段时间,这几天终于撸完了生信技能树B站的RNA-seq视频。本人黑眼圈纯粹是熬夜写生信代码所致,无任何不良嗜好,请大家放心交友。将一台老电脑改装成了win+linux双系统,取了1万条reads进行处理顺完了这个教程:原创10000+生信教程大神给你的RNA实战视频演练。然后再完整的顺完这个教程 一个RNA-seq实战-超级简单-2小时搞定!。破电脑可怜的6...
用limma包的voom方法来做RNA-seq 差异分析大家都知道,这十几年来最流行的差异分析软件就是R的limma包了,但是它以前只支持microarray的表达数据。考虑到大家都熟悉了它,它又发了一个voom的方法,让它从此支持RNA-seq的count数据啦!大家都知道芯片数据跟RNA-seq数据的本质就是value的分布不一样,所以各种针对RNA-seq的差异分析包也就是提出来一个新的normalization方法而已。而我们limma本身就提出了一个voom的方法来对RNA-seq数据进行
Gym 102028A - Xu Xiake in Henan ProvinceD - Keiichi Tsuchiya the Drift KingE - Resistors in ParallelF - HoneycombI - DistanceA - Xu Xiake in Henan Province题目链接题意t组查询,每组给出4个数,代表去过4个地方,如果4个数都是0,输出Typically Otaku,如果有3个0,输出Eye-opener,如果有2个0,输出Young Travell
inference1. sigmoid 出左上和右下角点的heatmaptl_heat = torch.sigmoid(tl_heat) br_heat = torch.sigmoid(br_heat)2. 然后进行一个NMS 也就是最大池化tl_heat = _nms(tl_heat, kernel=kernel) br_heat = _nms(br_heat, ke...
终端:cd ~/.ssh// 生成SSH Key(你的邮箱):$ ssh-keygen -t rsa -C [email protected]// 回车后(输入密码):Generating public/private rsa key pair.Enter file in which to save the key (/Users/lizhimin/.ssh/id_rsa): Enter pa
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxExceptionpublic List<RiskControlInfoDto> findRiskControlRiskLevelByTime(String planId,Date startTime, Date endTime) { StringBuilder sb = new StringBuilder();
转自:http://blog.csdn.net/liubingzhao/article/details/5563113 Android中的SurfaceView类就是双缓冲机制。因此,开发游戏时尽量使用SurfaceView而不要使用View,这样的话效率较高,而且Surf
参考:懒人操作之从TCGA批量下载RNASeq数据1.登录TCGA数据获取网站:https://portal.gdc.cancer.gov/。搜索GBM,选择TCGA-GBM2.选择下载的数据类型:(我需要下载的是RNA-Seq数据)。鼠标右键点击打开新页面4.对数据进行进一步筛选:(可根据自己需求筛选)5.将所有文件添加到购物车:(此购物车非彼购物车,这是不花钱的购物车)6.点击下载即可:...
https://www.2cto.com/net/201801/714420.html转载于:https://www.cnblogs.com/xiaoxiaoxiaoxue/p/9989695.html
官网:https://github.com/zsh-users/zsh-syntax-highlighting安装:git clone https://github.com/zsh-users/zsh-syntax-highlighting.git echo "source ${(q-)PWD}/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" >> ${ZDOTDIR:-$HOME}/.zshrc生效:sour..
Jimmy老师主要演示了四种比对工具,分别为hisat2、subjunc、bowtie2、bwa。除了subjunc能够直接生成bam文件外,这些软件的用法都很相似。需要根据自己的需求来选择对应的软件。这里以使用hisat2为例。