c++多态

多态:是指有多种形态

多态有两大类:

1.静态多态:函数重载,运算符重载,复用函数名(编译阶段确定地址)

2.动态多态:子类和虚函数实现运行时的多态(运行阶段确定地址)

细分以下多态:

  1. 参数多态(函数模板和类模板)
  2. 包含多态  (virtual)
  3. 重载多态(重载多态是指函数名相同,但函数的参数个数或者类型不同的函数构成多态)
  4. 强制多态(强制类型转换)
其中:
  1. 类型参数化多态和包含多态统称为一般多态性
  2. 重载多态和强制多态统称为特殊多态性

静态多态(编译阶段提前标定,当父类指针或引用接收之类对象时)会在编译阶段提前绑定父类中的函数

class A
{
public:
	void show(){cout << "父类show()" << endl;}
};
class B :public A
{
public:
	void show(){cout << "子类show()" << endl;}
};
//父类引用可以接受子类对象
void show1(A &a)
{
	a.show();
}
//父类指针接收子类对象
void show2(A*p)
{
	p->show();
}

int main()
{
	B b;
	//结果输出 父类的show 因为编译阶段 show1函数提早绑定了父类地址
	show1(b);
	B *b1 = new B;
    //结果输出 父类的show 因为编译阶段 show1函数提早绑定了父类地址
	show2(b1);
	return 0;
}

 在这里我们发现,我声明的时一个子类对象,但有父类指针或引用去接收时,调用的函数却是父类的,为了解决这个问题,c++提供了虚函数的概念。

虚函数:

虚函数 是在基类中使用关键字  virtual 声明的函数,在派生类中重写这个函数,当父类指针(引用)接收子类对象时,会采用动态多态,在运行阶段确定地址,会使用子类的函数
子类重写父类虚函数时:子类的virtual可加可不加

动态多态:

class A
{
public:
	virtual void show(){cout << "父类show()" << endl;}//声明为虚函数
};
class B :public A
{
public:
	void show(){cout << "子类show()" << endl;}
};
//父类引用接受子类对象
void show1(A &a)
{
	a.show();
}
//父类指针接收子类对象
void show2(A*p)
{
	p->show();
}

int main()
{
	B b;
	//结果输出 子类的show 因为运行阶段 show1函数最后绑定了子类地址
	show1(b);
	B *b1 = new B;
	//结果输出 子类的show 因为运行阶段 show1函数最后绑定了子类地址
	show2(b1);
	return 0;
}

 C++提供多态的目的是:可以通过基类指针对所有派生类(包括直接派生和间接派生)的成员变量和成员函数进行“全方位”的访问,尤其是成员函数。如果没有多态,我们只能访问成员变量。

纯虚函数:是一个在基类中声明的虚函数,在类中没有定义具体的操作内容。

格式:

virtual 函数类型 函数名(参数)=0;

声明为纯虚函数,则该函数可以不用实现 (也可以实现),拥有纯虚函数的类也称为 抽象类

1.抽象类不能实例化,但可以定义一个抽象类 指针和引用

2.子类一般需要重写抽象类的纯虚函数,不然也成抽象类

class person
{
public:
    //纯虚函数
    virtual void show() = 0;

    int age;
};
class son:public person
{
public:
    //重写父类的纯虚函数  子类未重写的话也为抽象类
    void show()
    {
        age = 20;
        cout << "son中age的值" << this->age << endl;
    }
};

int main()
{
    //纯虚函数不能实例化
    //person p;//报错
    //但可以使用指针和引用
    //vector 为容器,类似数组
    vector<person*>p;
    vector<person&>p1;
    //非纯虚函数可以实例化
    son s;
    return 0;
}

什么样的函数不能声明为虚函数?

  1. 不能被继承的函数。
  2. 不能被重写的函数。
  3. 普通函数,友元函数,构造函数,内联成员函数,静态成员函数

1)普通函数

普通函数不属于成员函数,是不能被继承的。普通函数只能被重载,不能被重写,因此声明为虚函数没有意义。因为编译器会在编译时绑定函数。

而多态体现在运行时绑定。通常通过基类指针指向子类对象实现多态。

2)友元函数

友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。

3)构造函数

首先说下什么是构造函数,构造函数是用来初始化对象的。假如子类可以继承基类构造函数,那么子类对象的构造将使用基类的构造函数,而基类构造函数并不知道子类的有什么成员,显然是不符合语义的。从另外一个角度来讲,多态是通过基类指针指向子类对象来实现多态的,在对象构造之前并没有对象产生,因此无法使用多态特性,这是矛盾的。因此构造函数不允许继承。

4)内联成员函数

我们需要知道内联函数就是为了在代码中直接展开,减少函数调用花费的代价。也就是说内联函数是在编译时展开的。而虚函数是为了实现多态,是在运行时绑定的。因此显然内联函数和多态的特性相违背。

5)静态成员函数

首先静态成员函数理论是可继承的。但是静态成员函数是编译时确定的,无法动态绑定,不支持多态,因此不能被重写,也就不能被声明为虚函数。

虚析构和纯虚析构:

  1. 虚析构是把析构函数设置为虚函数,纯虚析构的话,是把析构函数弄成纯虚函数
  2. 虚析构和纯虚析构都需要实现
  3. 纯虚析构在类内声明,类外实现
当子类开辟到堆区时,父类指针释放时无法调用子类析构代码
虚析构的作用:当父类指针(或引用)接收子类在堆区生成的对象时,父类可以调用子类的析构函数

当父类析构不是虚析构时:(delete 父类指针时不会调用子类的析构函数)

class person
{
public:
	person()
	{
		cout << "person的构造函数" << endl;
	}
	//纯虚函数
	virtual void show() = 0;
	//析构
	~person()
	{
		cout << "person的析构函数" << endl;
	}

};
class son :public person
{
public:
	son(int a)
	{
		age = new int(a);
		cout << "son的构造函数" << endl;
	}
	//重写父类的纯虚函数  子类未重写的话也为抽象类
	void show()
	{
		cout << "son中age的值" << this->age << endl;
	}
	~son()
	{
		if (age != NULL)
		{
			delete age;
			age = NULL;
		}
		cout << "son的析构函数" << endl;
	}
	int* age;
};

 把父类析构函数设置为虚析构时:

	//虚析构
	 virtual ~person()
	{
		cout << "person的析构函数" << endl;
	}

 把父类析构函数设置为纯虚析构时:

//纯虚析构   类内声明
virtual ~person() = 0;


//类外实现
person::~person()
{
	cout << "person的纯虚析构函数" << endl;
}

正在上传…重新上传取消正在上传…重新上传取消正在上传…重新上传取消正在上传…重新上传取消

虚函数和纯虚函数的权限:

  1. 虚函数可以为private,并且可以被子类覆盖(因为虚函数表的传递),但子类不能调用父类的private虚函数。虚函数的重载性和它声明的权限无关。
  2. virtual修饰符则强调父类的成员函数可以在子类中被重写,因为重写之时并没有与父类发生任何的调用关系,故而重写是被允许的。
  3. 纯虚函数可以设计成私有的,不过这样不允许在本类之外的非友元函数中直接调用它,子类中只有覆盖这种纯虚函数的义务,却没有调用它的权利。
class person
{
public:
private:
	virtual void show()
	{
		cout << "父类的show()函数" << endl;
	}
};

class son :public person
{
public:
	void show()
	{
		cout << "子类的show()函数" << endl;
	}
};

int main()
{
	son s;
	s.person::show();//子类无法访问父类的私有成员
	return 0;
}

运算符重载:

运算符重载:是对已有的运算符赋予多重定义,使用同一个运算符用于不同类型的数据有不一样的解决方法

c++规定: =    [ ]   ()  ->  这四个运算符只能被重载为类的非静态成员函数重载

其他的可以被友元重载

解释:

其他运算符重载函数都会更具参数类型或数目进行精确匹配,

这4个不具有这总检查功能,使用友元定义就会出错

不能被重载的运算符: 

sizeof      : ?      .      ::

长度运算符     三目运算符     成员选择符    域解析符

运算符重载的关键词:operator 

运算符重载规则:

  1. 只能重载C++已有的运算符
  2. 重载的功能应该与原有功能相似(一般不要 重载 + 号用 - 号的作用)
  3. 重载子后,优先级和结合性不变 

以下是重载运算符的示例:

+号的重载(-号和+号类似)

成员函数:
	person operator+(const person &p)
	{
		this->age += p.age;
		this->height += p.height;
		return *this;
	}

	int age;
	int height;

全局函数:

person operator+(const person &p,const person &p1)
{
	person p2;
	p2.age = p.age + p1.age;
	p2.height = p.height + p1.height;
	return p2;
}

想要实现连续运算:可以返回类的引用即可实现

全局函数: 

person& operator+(const person &p,const person &p1)
{
	person p2;
	p2.age = p.age + p1.age;
	p2.height = p.height + p1.height;
	return p2;
}

 ++重载:(--和++类似)

前置++:++x


	person& operator++()
	{
		age++;
		height++;
		return *this;
	}

	int age;
	int height;

后置++:x++

	//int为占位参数用来区分 前置++和后置++
	person& operator++(int)
	{
		person p = *this;
		age++;
		height++;
		return p;
	}

 重载<<运算符:

一般用全局函数实现,成员函数难以实现

cout 的类为 ostream 输出流类

cin   的类为  istream  输出流类

class person
{
public:
	//有参构造
	person(int a,string b):age(a),name(b){}

	int age;
	string name;
};
//全局函数重载<<
ostream& operator<<(ostream& o,const person &p1)
{
	cout << "年龄为:" << p1.age << "姓名为:" << p1.name << endl;
    return cout;
}
int main()
{
	person p(10, "a");
	//直接输出 age和name
	cout << p<<endl;
	return 0;
}

重载new和delete

重载的格式:

  1. 返回值为 void*
  2. 参数为size_t size,表示开辟空间的大小

重载new:(成员函数)

void * operator new(size_t size)
	{
		void* p = malloc(size);
	}

重载delete:(成员函数)

void operator delete(void *)
	{
		free(p);
	}

 重载()

  1. 一种为仿函数
  2. 直接重载
class person
{
public:
	//仿函数:用法与函数类似
	void operator()()
	{
		cout << "仿函数" << endl;
	}
	int operator()(int a, int b)
	{
		return a + b;
	}
};

int main()
{
	person p;
	//输出一段话
	p();
	//输出两个数的和
	p(10, 20);
	return 0;
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值