新河网站建设,淘宝店铺装修免费模板,上国外的网站很慢,广州招聘网站制作类的多态
多态概念入门
#include iostream
using namespace std;/*
多态的前提: 拥有继承关系的类中有相同的函数(返回类型、函数名、形参列表)
多态解决的问题#xff1a;1、派生类的对象被赋值给基类对象时2、派生类的对象初始化基类的引用时3、基类的指针指向派生…类的多态
多态概念入门
#include iostream
using namespace std;/*
多态的前提: 拥有继承关系的类中有相同的函数(返回类型、函数名、形参列表)
多态解决的问题1、派生类的对象被赋值给基类对象时2、派生类的对象初始化基类的引用时3、基类的指针指向派生类时函数调用的问题
总结没有多态时看类型。有多态时看内存。
*/class A
{
public:A() {cout A endl;}A(const A a) {cout A(const A a) endl;}void fun() {cout A::fun() endl;}
};class B : public A
{
public:B() {cout B endl;}B(const B b) {cout B(const B b) endl;}void fun() {cout B::fun() endl;}
};int main(int argc, char *argv[])
{B b;// 1、派生类的对象可以被赋值给基类对象A a b;a.fun();// 2、派生类的对象可以初始化基类的引用A a1 b;a1.fun();// 3、指向基类的指针也可以指向派生类A* a2 new B;a2-fun(); // 调用的是A类函数但本质确实B的空间return 0;
}
多态性
多态性例如函数重载同一个函数名会产生不同的结果而虚函数是实现多态性另一个重要途径
#include iostream
using namespace std;class A
{private:int a;public:virtual void f(); // 这是虚函数的一个地址而不是实际的函数地址所指向的的才是对应的函数
};int main()
{cout sizeof(A) endl; // 4 8 12 内存对齐原则 最终打印 16return 0;
}
普通虚函数
#include iostream
using namespace std;class Manmal
{public:// 注意点一// virtual 的三种情况为了书写规范基类和派生类必须都写// 1、基类有 派生类没有 可以构成虚函数// 2、基类有 派生类有 可以构成虚函数// 3、基类没有 派生类有 不可以构成虚函数virtual void sound() { cout 发出叫声 endl; }
};/*
注意点二
虚函数定义时不需要再次书写 virtual 关键字
void Manmal::sound()
{ cout 发出叫声 endl; }
*/class Cat : public Manmal
{public:// 注意点三// 此时派生类中拥有和基类相同的函数这一行为叫做函数的复写又称覆盖不是重载// 函数复写的最大特点基类的指针或引用可以忽略基类已存在的版本直接调用派生类的版本virtual void sound() { cout 喵喵喵 endl; }
};class Dog : public Manmal
{public:// 注意点四// 普通虚函数派生类可以不进行复写当注释下方语句时派生类进行调用则会打印基类虚函数叫声// 理解基类的虚函数是所有派生类的共同特点当派生类不具备自己的特点时则会显示出共同特点virtual void sound() { cout 汪汪汪 endl; }
};// 可以处理一切哺乳动物发出声音这种行为
void feed(Manmal m)
{m.sound();
}
------------------------------------------------------------------------------------------------------------------
/*
void feed(Cat cat)
{cat.sound();
}void feed(Dog dog)
{dog.sound();
}
*/
------------------------------------------------------------------------------------------------------------------
int main()
{Cat *cat new Cat;Dog *dog new Dog;feed(*cat);feed(*dog);/*// 上述代码改写 并删除 feed 函数Manmal *cat new Cat;Manmal *dog new Dog;cat-sound();dog-sound();*/delete cat;delete dog;return 0;
}
纯虚函数
概念相较于普通虚函数基类中的虚函数必须实现纯虚函数可以不用实现基类中的虚函数也可以实现这个基类称之为抽象基类但是同时要求派生类必须实现虚函数代码否则不能定义对象仍旧为抽象基类。
#include iostream
using namespace std;class A // 抽象基类专门用于派生子类自己不能定义对象
{public:// 本质只有实现这个函数才能定义对象这是所有派生类产生的必须条件。如战斗机、明航客机、直升机等都必须能飞virtual void f() 0; // 纯虚函数定义形式
};int main()
{return 0;
}
虚函数与构造函数
构造函数不能是虚函数
虚函数意味着要被派生类的各类方法进行复写而派生类的构造并不能通过基类。
虚函数与析构函数
结论
若一个类不会派生新的类则析构函数只需普通函数即可
若一个类会派生出新的类则析构函数必须定义为虚函数
#include iostream
using namespace std;class Base
{public:virtual ~Base() { cout ~Base() endl; }
};class Drived1 : public Base
{public:virtual ~Drived1() { cout ~Drived1() endl; }
};class Drived2 : public Base
{public:virtual ~Drived2() { cout ~Drived2() endl; }
};int main()
{// 若不将析构函数定义为虚函数那么派生类析构时就无法调用派生类的析构函数不能产生穿透// 析构函数相较于普通函数虚函数复写时名字可以不同因为一个类只有一个析构函数Base *d1 new Drived1;Base *d2 new Drived2;delete d1;delete d2;return 0;
}
运算符重载与友元
运算符函数
在 c 中操作数包含类对象时运算符操作就是一个函数。除了赋值运算符默认之外其余都需要进行重载才能使用。
class A
{public:// 重载了 运算符const A operator(const A b); // const 是为了保证和普通运算符运算方式一致如(ab)c 不应该被满足
};int main()
{A a, b;a b; // ----- a.operator(b)}/*
返回值类型A
函数名operator
参数列表const A r
*/
类成员运算符重载
#include iostream
using namespace std;class Time
{private:int hour;int minute;int second;public:Time(int h 0, int m 0, int s 0): hour(h), minute(m), second(s){};// 加法运算符操作函数重载 (t1 t2)const Time operator(const Time r);// 显示时间void show();
};const Time Time::operator(const Time r)
{Time tmp;tmp.second this-second r.second;if(tmp.second 60){tmp.minute;tmp.second - 60;}tmp.minute this-minute r.minute;if(tmp.minute 60){tmp.hour;tmp.minute - 60;}tmp.hour this-hour r.hour;tmp.hour % 24;return tmp;
}void Time::show()
{cout hour : minute : second endl;
}int main()
{Time t1(1, 50, 33);Time t2(0, 20, 33);Time t3 t1 t2;t3.show();return 0;
}
非类成员运算符重载
#include iostream
using namespace std;class Time
{public:int hour;int minute;int second;public:Time(int h 0, int m 0, int s 0): hour(h), minute(m), second(s){};// 加法运算符操作函数重载 (t1 t2)const Time operator(const Time r);// 类内输出运算符操作函数重载ostream operator(ostream out); // 显示时间void show();
};const Time Time::operator(const Time r)
{Time tmp;tmp.second this-second r.second;if(tmp.second 60){tmp.minute;tmp.second - 60;}tmp.minute this-minute r.minute;if(tmp.minute 60){tmp.hour;tmp.minute - 60;}tmp.hour this-hour r.hour;tmp.hour % 24;return tmp;
}void Time::show()
{cout hour : minute : second endl;
}// 类内输出运算符操作函数重载
ostream Time::operator(ostream out)
{out hour : minute : second endl;return out;
}// 类外输出运算符操作函数重载
ostream operator(ostream out, Time t) // 由于是类外重载所以有多少参数就需要传入多少个
{// 存在一个矛盾此时需要调用类内成员所以类内成员必须为 public但是这并不符合类成员数据原则out t.hour : t.minute : t.second endl;return out;
}int main()
{Time t1(1, 50, 33);Time t2(0, 20, 33);Time t3 t1 t2;t3.show();// 显然此时类外重载更加匹配平时使用的习惯t3 cout; // 类内重载运算符cout t3 endl; // 类外重载运算符return 0;
}注意1、定义非类成员运算符函数重载时必须保证至少有一个参数是自定义类型2、运算符的操作数必须从左到右一次填入参数列表中
上述重载存在着一个问题就是进行类外重载时必须要让类内部成员数据权限变成public而这并不符合类成员权限原则。所以需要借助后续的友元来进行解决。
前缀运算符重载
#include iostream
using namespace std;class Time
{public:int hour;int minute;int second;public:Time(int h 0, int m 0, int s 0): hour(h), minute(m), second(s){};// 加法运算符操作函数重载 (t1 t2)const Time operator(const Time r);// 类内输出运算符操作函数重载ostream operator(ostream out);// 前缀运算符操作重载Time operator();// 显示时间void show();
};const Time Time::operator(const Time r)
{Time tmp;tmp.second this-second r.second;if(tmp.second 60){tmp.minute;tmp.second - 60;}tmp.minute this-minute r.minute;if(tmp.minute 60){tmp.hour;tmp.minute - 60;}tmp.hour this-hour r.hour;tmp.hour % 24;return tmp;
}void Time::show()
{cout hour : minute : second endl;
}// 类内输出运算符操作函数重载
ostream Time::operator(ostream out)
{out hour : minute : second endl;return out;
}// 类外输出运算符操作函数重载
ostream operator(ostream out, Time t) // 由于是类外重载所以有多少参数就需要传入多少个
{// 设置宽度为 2不足则填充为 0out.width(2);out.fill(0);// 存在一个矛盾此时需要调用类内成员所以类内成员必须为 public但是这并不符合类成员数据原则out t.hour : t.minute : t.second endl;return out;
}// 前缀运算符操作重载
Time Time::operator()
{second;second second 60 ? (minute, 0) : second;minute minute 60 ? (hour, 0) : minute;hour % 24;return *this;
}int main()
{Time t1(1, 50, 33);Time t2(0, 20, 33);Time t3 t1 t2;cout t3.show() endl;t3.show();// 显然此时类外重载更加匹配平时使用的习惯cout t3 cout endl;t3 cout; // 类内重载运算符cout cout t3 endl endl;cout t3 endl; // 类外重载运算符t1;cout t1 endl;cout t1 endl;return 0;
}
后缀运算符重载
#include iostream
using namespace std;class Time
{public:int hour;int minute;int second;public:Time(int h 0, int m 0, int s 0): hour(h), minute(m), second(s){};// 加法运算符操作函数重载 (t1 t2)const Time operator(const Time r);// 类内输出运算符操作函数重载ostream operator(ostream out);// 前缀运算符操作重载Time operator();// 后缀运算符操作重载const Time operator(int); // int 没有实际作用只是用于区分前缀还是后缀// 显示时间void show();
};const Time Time::operator(const Time r)
{Time tmp;tmp.second this-second r.second;if(tmp.second 60){tmp.minute;tmp.second - 60;}tmp.minute this-minute r.minute;if(tmp.minute 60){tmp.hour;tmp.minute - 60;}tmp.hour this-hour r.hour;tmp.hour % 24;return tmp;
}void Time::show()
{cout hour : minute : second endl;
}// 类内输出运算符操作函数重载
ostream Time::operator(ostream out)
{out hour : minute : second endl;return out;
}// 类外输出运算符操作函数重载
ostream operator(ostream out, Time t) // 由于是类外重载所以有多少参数就需要传入多少个
{// 设置宽度为 2不足则填充为 0out.width(2);out.fill(0);// 存在一个矛盾此时需要调用类内成员所以类内成员必须为 public但是这并不符合类成员数据原则out t.hour : t.minute : t.second endl;return out;
}// 前缀运算符操作重载
Time Time::operator()
{second;second second 60 ? (minute, 0) : second;minute minute 60 ? (hour, 0) : minute;hour % 24;return *this;
}// 后缀运算符操作重载
const Time Time::operator(int)
{// 作出说明1相较于前缀运算此时加入 const 是防止出现 t1 t2这一点相较于未重载前冲突// 作出说明2为什么不返回引用因为在函数内部创建了一个临时变量来保存原值并返回那么函数结束会释放销毁返回引用则找不到对应的地址所以直接返回临时变量的内部数值Time tmp *this;second;second second 60 ? (minute, 0) : second;minute minute 60 ? (hour, 0) : minute;hour % 24;return tmp;
}int main()
{Time t1(1, 50, 33);Time t2(0, 20, 33);Time t3 t1 t2;cout t3.show() endl;t3.show();// 显然此时类外重载更加匹配平时使用的习惯cout t3 cout endl;t3 cout; // 类内重载运算符cout cout t3 endl endl;cout t3 endl; // 类外重载运算符t1;cout t1 endl;cout t1 endl;t1;cout t1 endl;cout t1 endl;return 0;
}
友元函数
目的在类外部非本类成员对类内部非公有数据进行访问。
例如遥控器仅仅通过自身按钮而非电视机公开按钮去操纵电视机
友元函数并没有破坏封装性原则应为决定函数是否为友元函数的对象还是类本身
#include iostream
using namespace std;class A
{private:int x;public:A(int x 0) : x(x){}// 声明一个友元函数friend void show(const A r);
};void show(const A r)
{cout r.x endl;
}int main()
{A a(6);// 友元函数的调用与普通函数一样不需要类对象引用show(a);return 0;
}注意1、友元函数可以直接访问类私有成员且友元函数定义不需要写 friend 关键字
修改上述非类成员类外函数运算符重载代码存在的问题如下
#include iostream
using namespace std;class Time
{private:int hour;int minute;int second;public:Time(int h 0, int m 0, int s 0): hour(h), minute(m), second(s){};// 类外输出运算符操作函数重载friend ostream operator(ostream out, Time t); // 显示时间void show();
};void Time::show()
{cout hour : minute : second endl;
}// 类外输出运算符操作函数重载
ostream operator(ostream out, Time t) // 由于是类外重载所以有多少参数就需要传入多少个
{out t.hour : t.minute : t.second endl;return out;
}int main()
{Time t1(1, 50, 33);cout t1 endl; // 类外重载运算符return 0;
}
友元类
#include iostream
using namespace std;class A
{private:int a;public:void show() { cout a endl; }// 将 B 类作为 A 类的友元类friend class B; // B 类中所有类方法都可以访问 A 类中所有成员数据
};class B
{public:void set_A(A r, int b) { r.a b; }
};int main()
{A a;B b;b.set_A(a, 6);a.show();return 0;
}
友元类方法
相较于友元类友元类方法缩小了范围意思就是在 A 类中只允许 B 类中的部分类方法来开放访问所有成员数据权限。
#include iostream
using namespace std;class A
{private:int a;public:void show() { cout a endl; }// 将 B 类中的部分类方法作为 A 类的友元类方法friend void B::set_A(A r, int b); // B 类中仅该类方法可以访问 A 类中所有成员数据
};class B
{public:void set_A(A r, int b) { r.a b; }
};int main()
{A a;B b;b.set_A(a, 6);a.show();return 0;
}注意上述方法极其容易出现未定义等现象要解决上述问题就要分文件处理主要可以分为 main.cpp A.cpp A.h B.cpp B.h