运算符重载
- 包括友元函数重载和成员函数重载
- 一元运算符重载
- -(负号)
- ++(自增)
- 二元运算符重载
- +(加号):第一个参数是*this
- <<(输出):只能通过友元函数重载,因为第一个参数只能是ostream&,不能是*this
- [](索引):只能通过成员函数重载,因为第一个参数只能是*this指针。
class Coordinate
{
// 输出重载
friend ostream& operator<<(ostream &out, const Coordinate &coor);// 友元函数重载
public:
Coordinate(int x, int y):m_iX(x), m_iY(y){
}
// 负号
Coordinate& operator-()
{
m_iX=-m_iX;
m_iY=-m_iY;
return *this;
}
// 前置自增
Coordinate& operator++()
{
m_iX++;
m_iY++;
return this*;
}
// 后置自增
Coordinate operator++(int)// int是个标识,用于区别于前置自增函数
{
Coordinate old(*this);
m_iX++;
m_iY++;
return old;
}
// 加号重载
Coordinate operator+(const Coordinate &coor)
{
Coordinate temp(0,0);
temp.m_iX=this->m_iX+coor.m_iX;
temp.m_iY=this->m_iY+coor.m_iY;
return temp;
}
// 索引重载
int operator[](int index)
{
if(index==0)
return m_iX;
if(index==1)
return m_iY;
}
private:
int m_iX;
int m_iY;
};
ostream& operator<<(ostream &out, const Coordinate &coor)
{
out<<coor.m_iX<<","<<coor.m_iY<<endl;
return out;
}
int main()
{
Coordinate coor1(3,5);
Coordinate coor2(4,7);
Coordinate coor3(0,0);
-coor1; // coor1.operator-();
++coor1;// coor1.operator++();
coor1++;// coor1.operator++(0);
coor3=coor1+coor2;// coor1.operator+(coor2);
cout<<coor1;// operator<<(cout,coor1);
cout<<coor1[0];// coor1.operator[](0);
return 0;
}
问题
class CMyString
{
public:
CMyString(char* pData=nullptr);
CMyString(const CMyString& str);
~CMyString();
private:
char* m_pData;
}
对上面的类重载赋值(=)运算符
CMyString& CMyString::operator=(const CMyString& str)// 注意传入的参数类型是常量引用,避免调用复制构造函数
{
// 如果不判断,若是自身则释放掉内存后无法赋值
if(this==&str)
return *this;
// 注意释放内存,防止内存泄漏
delete []m_pData;
m_pDate=nullptr;
// 注意strlen不包含结束符
m_pData=new char[strlen(str.m_pData)+1];
strcpy(m_pData, str.m_pData);
// 返回自身实例的引用,否则不能连续赋值
return *this;
}
函数模板与类模板
- 关键字:template、typename、class
- typename和class等价
使用
// 函数模板
template<class T>// 类型作为参数
T max(T a, T b)
{
return (a>b)?a:b;
}
template<int size>// 变量作为参数
void display()
{
cout<<size<<endl;
}
// 类模板
template<class T>
class MyArray
{
public:
void display(){
...}
private:
T *m_pArr;
}
template<class T>
void MyArray<T>::display()
{
...
}
int main()
{
int ival=max(100,99);
char cval=max<char>('A','B');
display<10>();
MyArray<int> arr;
arr.display();
}
- 当函数模板和类模板没有使用时,是不会产生任何代码数据的。只有当使用时才会实例化具体的模板函数和模板类,才会产生真正的代码。
可变参数模板:
问题
typename和class的区别
-
在声明模版参数时,class和typename关键是等价的,可以相互替换。
-
在涉及“嵌套依赖类型名”(nested dependent type name)的时候,必须用typename关键字去标识。
template <typename T>
void print2nd( const T& container)
{
T::const_iterator * x;
...
}
-
在print2nd这样一个模版函数中,变量x的类型依赖于模版参数T,因此它的具体类型只有在编译时的模版推导过程(template deduction)中才能够被确定。这样的类型称之为“嵌套依赖类型”(nested dependent type name)。
-
但是很遗憾,编译器并不知道const_iterator是一个类型还是一个静态变量。在默认情况下,C++标准会让编译器会把const_iterator做为一个静态变量处理。
-
typename就会显示告诉编译器T::const_iterator是一个类型,因此这一行的语义就是声明一个嵌套依赖类型的局部变量,而不是一个静态变量乘以x。
template < typename T>
void print2nd( const T& container)
{
typename T::const_iterator * x;
...
}
- 但是在继承列表或者成员初始化列表中的基类初始化时,可以不用typename去标识“嵌套依赖类型”。
template < typename T>
class Derived: public Base<T>::Nested //in base class list, no typename
{
public :
explicit Derived(int x)
: Base<T>::Nested(x) //in member init list, no typename
{
typename Base<T>::Nested temp; //nested dependent type, need typename
}
};
可调用对象
- C++中有几种可调用对象:函数,函数指针,lambda表达式,bind创建的对象,以及重载了函数调用符的类。
左值与右值
-
C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,那些可以在多条语句中使用的对象。所有的变量都满足这个定义,在多条代码中都可以使用,都是左值。
-
右值是指临时的对象,它们只在当前的语句中有效。
int i = 0; // i 是左值,0 是临时值,就是右值。在下面的代码中,i 可以被引用,0 就不可以了。
((i>0) ? i : j) = 1;// 右值也可以出现在赋值表达式的左边,但是不能作为赋值的对象
右值引用、转移构造函数、std::move、精确传递(完美转发):
右值引用与转移语义