从C过度到C++,你必须要掌握的知识点

内容原创,未经本人同意请勿转载。联系本人:jianshu_kevin@126.com
博主自营店铺(豆皓妈妈的小店–店铺5元收藏券,先到先得)

##1,virtual函数
函数之前加上virtual关键字就表示该函数为虚函数,在派生类中通过重写该虚函数来实现对基类函数的覆盖。

//基类中定义virtual函数
class base
{
public:
    virtual void fun() {cout<<"BASE";}
};
//派生类中覆盖fun函数
class derived: base
{
public:
	//不管该函数之前是否添加virtual字段,该函数都是虚函数
	void fun() {cout<<"DERIVED";}
};
/*多态使用*/
int main()
{
	base* d = new derived();
	/*类的多态,调用的是派生类中的fun函数*/
	d->fun();
}
//输出 
DERIVED
//
void call_fun(base* b)
{
	//如果b是base类的实例,就调用base中的fun
	//如果b是derived类的实例,就调用derived中的fun
	b->fun();
}

为何"虚"—动态联编

virtual函数用到了动态联编推迟联编的技术,virtual函数在编译的时候是无法确定的,而是在运行的时候被确定的。
编译器在发现类中有virtual函数的时候,就会为该类分配一个VTABLE函数指针数组,这个数组里存放了类中的所有虚函数。

一个类只有一个VTABLE,不管有多少个实例
派生类有各自的VTABLE
同一个虚函数在基类和派生类的VTABLE的相同位置
编译器在编译的时候为每个实例在内存中分配一个vptr字段,该字段指向本实例的VTABLE

    void call_fun(base* b)
	{
		//如果b是base类的实例,就调用base中的fun
		//如果b是derived类的实例,就调用derived中的fun
		//编译后该函数b->fun();变成
		(base->vptr[1])();
		//这样根据传递进来的实例不同,就调用不同的函数。
	}

纯虚函数(interface类)

	class base
	{
	public:
		virtual fun() = 0; //0标志一个虚函数为纯虚函数	
	}

纯虚函数表示该类是一个抽象类,无法被实例化,只能被派生类继承覆盖。用来规范派生类,这就是所谓的“接口”类,用来约束派生类需要实现这些函数。
##2,命令空间

定义

	namespace ns1
	{
		int a;
		int b;
		class base{};
		void fun(){}
	}

分离式定义

one.h

	#ifndef TWO_H_
	#define TWO_H_
	namespace two
	{
		void say();
	}
	#endif

two.h

	#ifndef TWO_H_
	#define TWO_H_
	namespace two
	{
		void say();
	};
	#endif

one_two.cpp

	#include <iostream>
	#include "one.h"
	#include "two.h"
	void one::say()
	{
		cout<<"one say\r\n";
	}
	void two::say()
	{
		cout<<"two say\r\n";
	}
	//如果声明的空间有类如何实现????

使用

若想使用某个标识符,using 空间名::标识符;
若想使用改namespace下的所有标识符 using namespace 空间名;

	//方法1
	using namespace one;
	//方法2
	using one::say;

自定义空间名使用

	#include <iostream>
	#include <string>
	using namespace std;
	using namespace one;
	using namespace two;

	//全局函数
	void say()
	{
		cout<<"global say\r\n";
	}
	int main()
	{
		//全局函数,一定要加上::
		//否则会出现 错误:调用重载的‘say()’有歧义
		::say();
		one::say();
		two::say();
	}

##3,模板
模板可以实现逻辑相同,但是数据类型不同的代码复制。当用户使用模板时,参数由编译器决定,这很像
模板分为函数模板类模板

函数模板

定义&使用

    template <类型参数表> 返回类型 函数名(形参列表) {函数实体}
	
	template <typename T> void print(const T& var)
	{
		cout<<var<<endl;
	}

	int main()
	{
		String a("hello template");
		int num = 123;
		print(a);
		print(num);
	}
	/**********多个参数***********/
	template <class T> T min(T ii, T jj, T kk)
	{
		T temp;
		if(ii < jj) temp = ii;
		else temp = jj;
		if(temp > kk) temp = kk;
		return temp;
	}
	int main()
	{
		int minNum = min(100, 30, 102);
		cout<<minNum<<endl;
		char minChar = min('z', 'a', 'h');
		cout<<minChar<<endl;
		return 0;
	}

类模板

##4,操作符重载

4.1,怎么定义该函数

使用operator xx(xx表示操作符,例如==,+,-,等

	class person{
	private:
		int age;
	public:
		person(int a):age(a){};
		inline operator == (const person& p) const;
	};

	inline person::operator==(const person& p) const
	{
		if(this->age == p.age)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	using namespace std;
	void main()
	{
		person p1(10);
		person p2(20);
		
		if(p1 == p2)
		{
			cout<<"the age equal"<<endl;
		}
		else
		{
			cout<<"the age different"<<endl;
		}
	}

4.2,为什么要重载

对于系统的所有操作符,一般情况下,只支持基本数据类型和标准库中提供的class,对于用户自己定义的class,如果想支持基本操作,比如比较大小,判断是否相等,等等,则需要用户自己来定义关于这个操作符的具体实现。比如,判断两个人是否一样大,我们默认的规则是按照其年龄来比较,所以,在设计person 这个class的时候,我们需要考虑操作符==,而且,根据刚才的分析,比较的依据应该是age。那么为什么叫重载呢?这是因为,在编译器实现的时候,已经为我们提供了这个操作符的基本数据类型实现版本,但是现在他的操作数变成了用户定义的数据类型class,所以,需要用户自己来提供该参数版本的实现。

4.3,操作符重载为全局函数

估计没人会这么使用

对于全局的操作符重载函数,左操作数的参数必须被显式的定义。

bool operator == (person const &p1, person const &p2);

如何决定使用全局还是类操作符重载函数呢?

  • 1 左操作符和比较对象是不是同一个类型
  • 2 C++要求,=、[]、()、->、必须定义为类操作符重载函数

##5,重载函数
###5.1 什么叫重载函数?
在同一个作用域内,可以有一组具有名字相同,参数不同的一组函数。重载函数用来命名一组功能相似的函数。
###5.2 为什么要用重载函数

  • 必须写很多函数名,来完成功能相似的一组函数
  • 类构造函数,如果没有重载。那如果要实例化不同的类是比较麻烦的。
  • 操作符重载本身也是函数重载,丰富了已有操作符的功能。

5.3 编译器如何解决命名冲突

编译器会把不同参数名字相同的函数,用新的函数名取代。恩,其实也就还是不同名字,但是写起来方便很多

5.4 重写函数(override)

子类重新定义父类中有相同名称、相同参数虚函数

  • 被重新定义的函数不能为static函数
  • 重写的函数一定要完全相同(包括返回值、参数)
  • 重写的函数可以有不同的修饰符,例如在基类中是private,派生类可以写成public、protected

5.5 重定义函数(redefining)

子类重新定义父类中具有相同名字的函数(参数列表可以不同)。

##6,static类
###6.1 static成员函数
static数据成员是存储在程序的静态存储区,而并不是在栈空间上。独立于任何类的对象。

注意:static成员函数

  • 没有this指针。因为static成员函数不是任何对象的组成部分
  • 不能声明为const类型,不能访问非static成员,也不能访问static const成员。

###6.2 static成员

注意:在类中不能对static成员进行初始化

	class person{
	private:
		static int age;
		//static int age= 20; //错误:ISO C++ 不允许在类内初始化非常量静态成员'person::age'
		static string name;
	public:
		void print()
		{
			cout<<"name: "<<name<<" age: "<<age<<endl;
		}
	}

	int person::age = 30;
	string person::name = "kevin";

	int main()
	{
		//int person::age = 20;  错误:对限定名‘person::age’的使用无效
		person p;
		p.print();
		return 0;
	}

内容原创,未经本人同意请勿转载。联系本人:jianshu_kevin@126.com

##7,…

##8,this
类中的成员函数,都有一个附件的隐含实参,该实参(this)就是一个指向该类对象的指针。
##9,#include xx.h文件和xx有何区别

##10,访问权限
###三种访问权限

  • public 可以被任意实体访问
  • protected 只允许子类和本类成员函数访问
  • private 只允许本类成员函数访问

三种继承方式

  • public继承 不改变基类成员的访问权限
  • protected继承 使得基类中public变成protected,其他权限不变。
  • private继承 使得基类中所有成员权限变成private
	class base{};
	class deliverd : public base{}; //public继承

##11, 重载和覆盖
##12, new delete
##13, const成员函数
若将成员成员函数声明为const,则该函数不允许修改类的数据成员

1)const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;
2)非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;
3)作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应尽可能将该成员函数声明为const 成员函数。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值