1、操作符重载
听说过方法重载,还没听说过操作符也能重载。。。这个特性,Kotlin也有,但是我没怎么用过,Kotlin不愧是Kotlin把C++精华都抄了过去。 常用的操作符 + - [] <<>>,在系统中,我们的算数运算符只可以用来两个数相加。但是我们把它们重载了,就可以干一些我们的事情。
1.1 运算符重载
class Point {
private:
int x = 0;
int y = 0;
public:
Point() { }
Point(int x,int y) :x(x),y(y) {}
void setX(int x) {
this->x = x;
}
void setY(int y) {
this->y = y;
}
int getX() {
return this->x;
}
int getY() {
return this->y;
}
//类里边的写法,规范写法
Point operator + ( Point & point) {
Point newPoint;
int x =this->x +point.getX();
int y= this->y + point.getY();
newPoint.setX(x);
newPoint.setY(y);
return newPoint;
}
};
//类外边的写法
// Point operator + ( Point & point1, Point & point2) {
// Point newPoint;
// int x = point1.getX() +point2.getX();
// int y =point1.getY() +point2.getY();
// newPoint.setX(x);
// newPoint.setY(y);
// return newPoint;
// }
int main(int argc, char *argv[]) {
Point point1(100,200);
Point point2(200,300);
Point point =point1+point2;
cout << point.getX()<<"," << point.getY()<<endl;
return 0;
}
有两种模式,在类外边可以重载操作符,但是我们一般不这样写,规范的写法都是写在类里边的。
Point operator + ( Point & point1, Point & point2) 类外边的写法,操作符重载的两个参数,分别是操作符 + 左边的参数和右边的参数。
Point operator + ( Point & point) 类里边的写法 就一个参数,操作符+ 右边的参数。其他运算符同理。对于++ ,--这类运算符有点特殊。
// ++ 对象
void operator ++() {
this->x = this->x+1;
this->y =this->y+1;
}
//对象 ++
void operator ++ (int) {
this->x = this->x+1;
this->y =this->y+1;
}
1.2 重载输出运算符<< 实现连续打印自己的对象
friend ostream & operator << (ostream & _START, const Point & point) {
//系统的操作符
_START << point.x <<","<<point.y<<endl;
return _START;
}
int main(int argc, char *argv[]) {
Point point1(100,200);
Point point2(200,300);
Point point =point1+point2;
cout << point1 << point2 <<point;
return 0;
}
输出操作符,在用ostream 时,必须加上friend 这是规定 和其他操作符相比,就是要把第一个参数进行返回。这样就会形成一个闭环。实现连续打印自己的对象。 在很多框架或者Android系统源码里,操作符重载,参数大多都采用 常量引用的方式。这样做的好处,传进去的参数是只读的,保证数据安全,而且是引用,只给对象起了别名,并没有复制在方法入栈的时候,复制一份 Point的副本,这样可以节省很多系统资源,提升程序运行效率。如果传递对象,它会执行类的拷贝构造函数,拷贝一份副本,供方法内使用。
1.3 操作符重载案例,中括号重载[]
// 数组 [i] 系统已经对这个进行重载了, 相当于 * (arr+i)做了这件事,
//我们也重载一把这个操作符玩一玩
class ArrayClass {
private:
int size = 0; //c++没有默认值,我们要赋一个默认值
int *arryValue; //存放数组值
public:
void add(int value) {
arryValue[size] =value;//这个中括号 是系统的
size +=1;
}
int operator [] (int index) {
return this->arryValue[index];//系统的
}
int getSize() {
return this->size;
}
};
实现的共功能和系统的是一样的。
2、继承,二义性
继承和Java差不多,只说和Java不一样的地方。
2.1 必须执行父类的构造方法
如果一个类继承了某个类,子类的构造方法必须初始化父类有参数的的构造方法。 Base 'Person' is not initialized in this constructor
正确的代码
class Student :Person {
Student(char * name,int age):Person(name,age) {
}
};
2.2 C++子类访问父类成员的权限
在C++中,即使继承了父类,父类的公开成员属性在类外边也不能访问。
想要访问怎么办呢,在继承Person的时候,声明公开继承。
class Person {
public:
char * name;
int age=0;
public:
Person(char * name,int age):name(name),age(age){}
};
class Student : public Person {
Student(char * name,int age):Person(name,age) {
}
void printName() {
this->name ="aaa";
}
};
int main(int argc, char *argv[]) {
Student student("虾米",99);
student.name ="xiami";
}
如果父类的成员属性是私有的,不好意思,不仅类外访问不了,类内也不能访问。这下能理解友元函数的比喻了吧,其关系超越了亲情和爱情。友元函数是能访问私有变量的。
| 父类属性权限修饰词 | 子类继承时权限修饰词 | 子类本身对父类属性的访问权限 | 子类外边对父类属性的访问权限 |
|---|---|---|---|
| private | private | 不能访问 | 不能访问 |
| public | private | 能访问 | 不能访问 |
| private | public | 不能访问 | 不能访问 |
| public | public | 能访问 | 能访问 |
2.3 多继承,
在Java中是不允许多继承的,但是可以多实现,严格避免了二义性问题。但在C++中是可以多继承的。子类是可以有多个爸爸的。而且多继承,在编写代码的时候,会遇到有歧义的地方,也就是二义性。导致编译不通过,需要我们多注意一下。
class BaseActivity1 {
public:
void onCreate() {
cout << "BaseActivity1 onCreate"<<endl;
}
void onStart() {
cout << "BaseActivity1 onStart"<<endl;
}
};
class BaseActivity2{
public:
void onCreate() {
cout << "BaseActivity2 onCreate"<<endl;
}
void onStart() {
cout << "BaseActivity2 onStart"<<endl;
}
};
class MainActivity : public BaseActivity1 , public BaseActivity2{
public:
void onCreate() {
cout << "MainActivity onCreate"<<endl;
}
};
int main(int argc, char *argv[]) {
MainActivity mainActivity;
//此时还没产生二义性,
//优先匹配的子类的方法,重写了父类的方法 打印结果 MainActivity onCreate
mainActivity.onCreate();
//产生了二义性 Ambiguous function call. 调用方法有歧义
//我的两个爹都有 onStart方法,你让我调谁。。。。
//mainActivity.onStart();
//解决办法一,和onCreate一样,在子类重写父类方法。你们两个的我都不调,我调用我自己的
//解决办法二 指明调用那个爹的函数
mainActivity.BaseActivity1::onStart();
}
现在我们把关系搞得更复杂一些,子类不仅有两个爸爸,它还有一个爷爷。三层继承关系。
ps:我们在开发过程中,要严格避免出现二义性的,代码中的例子是我们认为制造出来的二义性,在真实开发过程中要避免这样干。
我们写一个类,Object 里面有个公开的成员变量number,BaseActivity1,和BaseActivity2 都公开继承 Object,MainActivity 公开继承 BaseActivity1,BaseActivity2,这时我们使用MainActivity的实例,去访问number,会产生二义性,原理和上边的是一样的。也是不知道去找那个父类去调用number。 此时我们的解决方案,由原来的两种变成了三种,将BaseActivity1.和BaseActivity2都变成虚基类。这样我们就可以访问了。
class Object {
public:
int number;
};
class BaseActivity1 : virtual public Object{};
class BaseActivity2 : virtual public Object{};
class MainActivity : public BaseActivity1 , public BaseActivity2{};
int main(int argc, char *argv[]) {
MainActivity mainActivity;
//会产生二义性,原理是一样的,因为BaseActivity1 BaseActivty2 都继承了Object;
mainActivity.number;
}
BaseActivity1 虚继承 Object ,BaseActivity2 虚继承 Object, BaseActivity1,BaseActivity2被称为虚基类。多个虚基类只拥有一份父类的成员变量,解决了二义性问题。忽然感觉Java和Kotlin好香。