友元的三种实现方法
- 全局函数做友元
- 类做友元
- 成员函数做友元
全局函数做友元
class Building {
friend void accessBuilding(Building *b);
public:
Building() {
this->m_sittingRoom = "客厅";
this->m_bedRoom = "卧室";
}
public:
string m_sittingRoom;
private:
string m_bedRoom;
};
void accessBuilding(Building *b) {
//Building b=Building();
cout << "accessing sittingroom>>>>" << b->m_sittingRoom << endl;
cout << "accessing sittingroom>>>>" << b->m_bedRoom << endl;
}
类做友元
class Building {
friend class Visitor;
public:
Building() {
this->m_sittingRoom = "客厅";
this->m_bedRoom = "卧室";
}
public:
string m_sittingRoom;
private:
string m_bedRoom;
};
class Visitor {
public:
void visit(Building* b);
};
void Visitor:: visit(Building* b) {
cout << "accessing sittingroom>>>>" << b->m_sittingRoom << endl;
cout << "accessing sittingroom>>>>" << b->m_bedRoom << endl;
}
int main() {
Building b;
Visitor v;
v.visit(&b);
system("pause");
return 0;
}
成员函数做友元
//告诉编译器,Building这个类后面会写
class Building;
class Visitor {
public:
void visit(Building* b);//visit可以访问
void visit2(Building* b);//visit1不可以访问
};
class Building {
friend void Visitor::visit(Building* b);
public:
Building() {
this->m_sittingRoom = "客厅";
this->m_bedRoom = "卧室";
}
public:
string m_sittingRoom;
private:
string m_bedRoom;
};
void Visitor:: visit(Building* b) {
cout << "accessing sittingroom>>>>" << b->m_sittingRoom << endl;
cout << "accessing sittingroom>>>>" << b->m_bedRoom << endl;
}
void Visitor::visit2(Building* b) {
cout << "accessing sittingroom>>>>" << b->m_sittingRoom << endl;
//cout << "accessing sittingroom>>>>" << b->m_bedRoom << endl;
}
int main() {
Building b;
Visitor v;
v.visit(&b);
system("pause");
return 0;
}
运算符重载
加号+重载
关键字:operator
class Person {
public:
Person(int a=0,int b=0) {
m_a = a;
m_b = b;
}
void printInfo();
int m_a;
int m_b;
//成员函数重载只要一个形参
Person operator+(Person &p) {
Person tmp;
tmp.m_a = this->m_a + p.m_a;
tmp.m_b = this->m_a + p.m_b;
return tmp;
}
};
void Person::printInfo() {
cout << this->m_a << endl;
cout << this->m_b << endl;
}
全局重载要两个形参
//Person operator+(Person& p1, Person& p2) {
// Person tmp;
// tmp.m_a = p1.m_a + p2.m_a;
// tmp.m_b = p1.m_b + p2.m_b;
// return tmp;
//}
int main() {
Person p1(10,10);
Person p2(20, 20);
Person p3 = p1 + p2;
p3.printInfo();
system("pause");
return 0;
}
左移<<重载
class Person {
public:
Person(int a=0,int b=0) {
m_a = a;
m_b = b;
}
void printInfo();
成员函数重载<< 最后是p<<cout,一般不用成员函数重载<<
//void operator<<(ostream &cout) {
// cout << this->m_a<<" "<<this->m_b << endl;
//}
int m_a;
int m_b;
};
void Person::printInfo() {
cout << this->m_a << endl;
cout << this->m_b << endl;
}
void operator <<(ostream &cout,Person p) {
cout << "m_a: " << p.m_a << " m_b: " << p.m_b << endl;
}
void test01() {
Person p1(10, 10);
cout << p1;
/*p1.operator<<(cout);*/
}
int main() {
test01();
system("pause");
return 0;
}
递增运算符++重载
class MyInt {
friend ostream& operator<<(ostream& cout, MyInt i);
public:
MyInt() {
m_num = 0;
}
//重置前置++,++之后应该返回的还是原对象,所以要引用
MyInt& operator++() {
m_num++;
return *this;
}
//重置后置++,区分前置和后置用int这个占位符,后置++返回值
MyInt operator++(int) {
//先记录原值
MyInt tmp= *this;
//递增
m_num++;
//返回原值
return tmp;
}
private:
int m_num;
};
ostream& operator<<(ostream &cout,MyInt i) {
cout << i.m_num;
return cout;
}
void test01() {
MyInt i;
cout << ++(++i)<<endl;
cout << i << endl;
}
void test02() {
MyInt i;
cout << i++ << endl;
cout << i << endl;
}
int main() {
//test01();
test02();
system("pause");
return 0;
}
赋值=重载
为了使用=时,直接进行深拷贝
class Person {
public:
Person(int a = 0, int b = 0) {
m_a = new int(a);//构造函数将数据初始化在堆区,深拷贝
m_b = new int(b);
}
~Person() {
if (m_a != NULL) {
delete m_a;
m_a = NULL;
}
if (m_b != NULL) {
delete m_b;
m_b = NULL;
}
}
void printInfo();
int *m_a;
int *m_b;
Person& operator=(Person &p1) {
//这样的写法是编译器提供的浅拷贝
/*this->m_a = p1.m_a;
this->m_b = p1.m_b;*/
//应该先判断有没有属性对象在堆区,如果有应该释放,再进行深拷贝
if (m_a != NULL) {
delete m_a;
m_a = NULL;
}
if (m_b != NULL) {
delete m_b;
m_b = NULL;
}
//深拷贝
m_a = new int(*p1.m_a);
m_b = new int(*p1.m_b);
//返回对象本身
return *this;
}
};
void Person::printInfo() {
cout << this->m_a << endl;
cout << this->m_b << endl;
}
void test01() {
Person p1(100, 100);
Person p2(50,50);
Person p3(20, 20);
p3 = p2 = p1;
cout << *p1.m_a << endl;
cout << *p2.m_a << endl;
cout << *p3.m_a << endl;
}
int main() {
test01();
system("pause");
return 0;
}
关系运算符==重载
bool operator==(Person p1,Person p2) {
if ((p1.pid == p2.pid) && (p1.pname == p2.pname)) {
return true;
}
else {
return false;
}
}
bool result2 = (p1==p2 == p3);
如果重载返回bool,就不能判断p1p2p3的式子
函数调用运算符()重载
- 由于重载后非常像函数调用,因此被称为仿函数
class Person {
friend Person& operator==(Person p1, Person p2);
public:
Person(int id,string name) {
this->pid = id;
this->pname = name;
}
void operator() (string text) {
cout << text << endl;
}
private:
int pid;
string pname;
};
void test01() {
Person p(0, "alex");
p("helloworld");
}
int main() {
test01();
system("pause");
return 0;
}
继承
基本语法
class Animal {
public:
void feed() {
cout << "feeding..." << endl;
}
void reproduction() {
cout << "reproduction..." << endl;
}
};
class Dog :public Animal {
public:
void bark() {
cout << "woaf!!!" << endl;
}
};
继承方式
-
public:子类不能访问父类的private,继承父类中其他的访问权限不变
-
protect:子类不能访问父类的private,继承父类中其他的访问权限变成protect
-
private:子类不能访问父类的private,继承父类中其他的访问权限变成private
继承中的对象模型
父类中所有的非静态static成员属性都会被子类继承
父类中的私有属性是被编译器隐藏的,但是确实也被继承了
查看对象模型
在VS 开发人员命令提示符中输入
cl /d1 reportSingleClassLayoutDog opencv_test.cpp
构造和析构顺序
输出
父类构造!
子类构造!
子类析构!
父类析构!
继承同名成员的处理方式(作用域)
class Animal {
public:
Animal() {
arg = 10;
}
void feed() {
cout << "feeding..." << endl;
}
void reproduction() {
cout << "base reproduction..." << endl;
}
void reproduction(int) {
cout << "base another reproduction..." << endl;
}
int arg;
private:
void god() {
cout << "不能继承" << endl;
}
};
class Dog :public Animal {
public:
Dog() {
arg = 100;
}
void bark() {
cout << "woaf!!!" << endl;
}
void reproduction() {
cout << "dog reproduction..." << endl;
}
int arg;
};
void test01() {
//Animal creature;
Dog dog1;
cout << dog1.arg << endl;
cout << dog1.Animal::arg << endl;//访问父类的非静态成员
dog1.reproduction();//直接访问是访问自己的成员
dog1.Animal::reproduction();//加上作用域访问父类的同名成员
dog1.Animal::reproduction(1);//子类中和父类重名的成员函数会隐藏掉父类中所有的成员函数,包括重载的,要想引用需要加作用域
}
int main() {
test01();
system("pause");
return 0;
}
同名静态成员处理
class Animal {
public:
static void reproduction() {
cout << "base reproduction..." << endl;
}
void reproduction(int) {
cout << "base another reproduction..." << endl;
}
static int arg;
private:
void god() {
cout << "不能继承" << endl;
}
};
int Animal::arg = 100;
class Dog :public Animal {
public:
static void reproduction() {
cout << "dog reproduction..." << endl;
}
static int arg;
};
int Dog::arg = 1000;
void test01() {
//通过对象访问静态成员变量
Dog dog1;
cout << dog1.arg << endl;
cout << dog1.Animal::arg << endl;//访问父类的非静态成员
//通过类名访问静态成员变量
cout << Dog::arg << endl;
cout << Dog::Animal::arg << endl;//第一个::代表通过类名访问 第二个::代表访问父类作用域下
//通过对象访问静态成员方法
cout << "通过对象访问静态成员方法" << endl;
dog1.reproduction();//直接访问是访问自己的成员
dog1.Animal::reproduction();//加上作用域访问父类的同名成员
dog1.Animal::reproduction(1);//子类中和父类重名的成员函数会隐藏掉父类中所有的成员函数,包括重载的,要想引用需要加作用域
//通过类名访问静态成员方法
cout << "通过对象访问静态成员方法" << endl;
Dog::reproduction();
Dog::Animal::reproduction();
}
多继承语法
多继承可能会引起父类中有同名成员出现,需要加作用域区分
class Animal {
public:
Animal() {
arg = 10;
}
int arg;
};
class Creature {
public:
Creature() {
arg = 20;
}
int arg;
};
class Dog :public Animal,public Creature {
public:
Dog() {
arg = 100;
}
int arg;
};
void test01() {
Dog dog1;
cout << dog1.arg << endl;
cout << dog1.Animal::arg << endl;
cout << dog1.Creature::arg << endl;
}
菱形继承
两个派生类继承同一个基类
又有某个类同时继承两个派生类
这种继承被称为菱形继承或钻石继承
问题
- 当菱形继承时,两个父类拥有相同的数据需要加作用域区分
void test01() {
FishMan fm;
//fm.arg = 200;//多继承,不明确,需要指定作用域
fm.Man::arg = 20;
fm.Fish::arg = 7;
cout << "fm.Man::arg: " << fm.Man::arg << endl;
cout << "fm.Fish::arg: " << fm.Fish::arg << endl;
}
- 但现在美人鱼有两个父类,继承了两个不同的arg成员变量,应该以哪一个为准呢,又是一个二义性**(利用虚继承解决)**
class Animal {
public:
Animal() {
arg = 10;
}
int arg;
};
//加上virtual之后的继承叫虚继承,此时称Animal为虚基类
class Man:virtual public Animal {
};
class Fish:virtual public Animal {
};
class FishMan:public Man,public Fish {
};
void test01() {
FishMan fm;
//fm.arg = 200;//多继承,不明确,需要指定作用域
fm.Man::arg = 20;
fm.Fish::arg = 7;//虚继承之后,arg就是几个类共享的数据了,先被赋值了20,后又被赋值了7,所以最终值为7
cout << "fm.Man::arg: " << fm.Man::arg << endl;
cout << "fm.Fish::arg: " << fm.Fish::arg << endl;
cout << "fm.arg: " << fm.arg << endl;
}
虚继承的原理
菱形继承的中间部分,就是Man和Fish的部分,实际上继承到的是一个叫vbptr的对象,又称虚基类指针(Virtual Base Pointer)
这里的虚基类指针都指向基类里面的arg成员变量,所有对这个指针的修改都只会修改基类的成员变量,所有消除了二义性。