[候捷课程记录]面向程序高级开发

候捷课1—面向对象高级开发

1.头文件

xxxx.h

#ifndef _XXXX_
#define _XXXX_

.
.
.

#endif

2.构造函数

利用初始值,初始列表构造

complax(double r=0,double i=0):re®,im(i)//初始化列表

{}

//如果在{}中写re=r,im=i;这种属于赋值 //会出现问题

在这里插入图片描述

//第二种构造函数重载不行,因为会产生歧义,与第一个重复构造

3.常量成员

如果只读不写就加const ,能加就加

4.参数传递

参数传递最好用引用(特殊情况传字符等,字符不加引用传递快)

如果不想修改传递值就加const

5.返回值传递

最好也要加&,但是不是所有情况都可以加

6.同个class中的object互为friend

7.什么时候不能返回引用

所谓的不可以返回局部变量的引用或指针,指的是不能返回局部变量的引用或地址给引用或指针。事实上还是看该地址的位置是否在该函数的栈区,若是在栈区,函数调用结束,该地址就被释放了。尽管会出现栈地址上的值没被销毁的问题,可能是该栈区还没被其他的函数堆栈

8.typename()临时对象

在这里插入图片描述

9.重载操作符

1.输入输出操作符最好设置在全局函数(<<与>>)

如果设置为成员函数,写法会与平常写法相反

// cout重载能不能写成成员函数,若能,写出函数原型,若不能,说明原因
 
#include <iostream>
using namespace std;
 
// cout做友元
class A;
ostream& operator<<(ostream &out, const A &a);
 
class A
{
    
    
	friend ostream& operator<<(ostream &out, const A &a);
public:
	A(int n = 0):m(n)
	{
    
    }
private:
	int m;
};
 
ostream& operator<<(ostream &out, const A &a)
{
    
    
	out << a.m;
	return out;
}
 
int main()
{
    
    
	A a;
	cout << a << endl;
	return 0;
}
 
 
 
 
// cout并不是不可以做成员函数,但是它的输出形式并不符合大家的习惯,所以还是不要把它写到成员函数里比较好
#include <iostream>
using namespace std;
 
class A
{
    
    
public:
	A(int n = 0) :m(n)
	{
    
    }
	// cout做成员函数
	ostream& operator<<(ostream &out)
	{
    
    
		out << m;
		return out;
	}
private:
	int m;
};
 
int main()
{
    
    
	A a;
	//注意输出格式
	a << cout << endl;
	return 0;
}

2.输出操作符<<重载不能加const,因为输出每时每刻都在改变

10.拷贝

1.拷贝构造:有指针情况要进行深拷贝

2.拷贝赋值:需要加一个检测自我赋值

在这里插入图片描述

11.栈与堆

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

12.继承

在这里插入图片描述

13.虚函数

在这里插入图片描述

14.prototype

如果一个父类很早就创造出来了但是想要创建未来子类对象时(通俗讲就是为了让父类学习知道未来子类的东西),他的子类可以自己创建自己,加上addPrototype(this);可以把子类原型放入父类创建的prototype的空间,加上clone();目的是return new 子类,就是说让父类看到子类的原型,然后通过原型调用副本。父类中有findandclone可以找到子类去复制子类副本,但是子类通过return new子类重新构造是调用另外一个#landsatimage(int)这个构造函数,防止重复上面的流程。

在这里插入图片描述

15.转换函数

class Fraction
{
    
    
public:
	Fraction(int num, int den=1) 
	  : m_numerator(num), m_denominator(den)
	{
    
     cout << m_numerator << ' ' << m_denominator << endl; }
	
 	operator double() const {
    
     
      return (double)m_numerator / m_denominator; 
 	}//转换函数


private:   
   int m_numerator;    //
   int m_denominator;  //
};
	
void test_conversion_functions()
{
    
    	
	cout << "test_conversion_functions()" << endl;
	
	Fraction f(3,5);
	
	double d = 4 + f;		//4.6
	cout << d << endl;
			
}

任何Fraction需要被转换为double类型的时候,自动调用double()函数进行转换。编译器在分析double d = 4 + f过程中判断4为整数,然后继续判断f,观察到f提供了double()函数,然后会对f进行double()操作,计算得到0.6,再与4相加,最后得到double类型的4.6。


class Fraction
{
    
    
public:
	Fraction(int num, int den=1) 
	  : m_numerator(num), m_denominator(den)
	{
    
     cout << m_numerator << ' ' << m_denominator << endl; }
 	
 	Fraction operator+(const Fraction& f) {
    
      
	   cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator <<  endl;  
	   return f; 
	} 

private:   
   int m_numerator;    //
   int m_denominator;  //
};
	
void test_conversion_functions()
{
    
    	
	cout << "test_conversion_functions()" << endl;
	
	Fraction f(3,5);
	
	
	 Fraction d2 = f + 4;		
}

定义了一个类,叫Fraction,类里面重载了“+”运算符,在f+4操作过程中,“4”被编译器隐式转换(构造函数)为Fraction对象,然后通过Fraction重载的“+”运算符参与运算。


class Fraction
{
    
    
public:
	Fraction(int num, int den=1) 
	  : m_numerator(num), m_denominator(den)
	{
    
     cout << m_numerator << ' ' << m_denominator << endl; }
	
 	operator double() const {
    
     
      return (double)m_numerator / m_denominator; 
 	}
 	
 	Fraction operator+(const Fraction& f) {
    
      
	   cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator <<  endl;  

	   return f; 
	} 

private:   
   int m_numerator;    
   int m_denominator; 
};
	
void test_conversion_functions()
{
    
    	
	cout << "test_conversion_functions()" << endl;
	
	Fraction f(3,5);
	
	double d = 4 + f;		//4.6
	cout << d << endl;
	
	//! Fraction d2 = f + 4;	 //ambiguous
    //会有歧义报错
}

在Fraction中增加了double()函数,将Fraction的两个成员变量进行除法运算,然后强制转化为double类型并返回结果,在f+4重载过程中编译器将报错,可以做出如下分析:

1、首先4被隐式转化(构造函数)为Fraction对象,然后通过重载的“+”运算符与“f”进行运算返回一个Frction对象;

2、首先4被隐式转化(构造函数)为Fraction对象,然后通过重载的“+”运算符与“f”运算后对进行double运算,最返回一个Frction对象;

3、。。。

所以编译器有至少两条路可以走,于是产生了二义性,报错。在构造函数Franction前加入explict关键字,隐式转换将取消,所以在执行d2 = f + 4过程中,f将调用double函数转换为0.6,然后与4相加变成4.6,由于构造函数取消隐公式转换,4无法转换为Fraction,但是加法需要4为Fraction,所以报错,而且4.6无法转换为Fraction,于是将报错。

代码如下

class Fraction
{
    
    
public:
	explicit Fraction(int num, int den=1) 
	  : m_numerator(num), m_denominator(den)
	{
    
     cout << m_numerator << ' ' << m_denominator << endl; }
	
 	operator double() const {
    
     
      return (double)m_numerator / m_denominator; 
 	}
 	
 	Fraction operator+(const Fraction& f) {
    
      
	   cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator <<  endl;  
	   return f; 
	} 


private:   
   int m_numerator;    //
   int m_denominator;  //
};
	
void test_conversion_functions()
{
    
    	
	cout << "test_conversion_functions()" << endl;
	
	Fraction f(3,5);
	
	double d = 4 + f;		//4.6
	cout << d << endl;
	
	//! Fraction d2 = f + 4;	 //在构造函数Franction前加入explict关键字,隐式转换将取消,所以在执行d2 = f + 4过程中,f将调用double函数转换为0.6,然后与4相加变成4.6,由于构造函数取消隐公式转换,4无法转换为Fraction,但是加法需要4为Fraction,所以报错,而且4.6无法转换为Fraction,于是将报错。		
}

16.智能指针

在这里插入图片描述

智能指针在语法上有三个很关键的地方,第一个是保存的外部指针,对应于上图的T* px,这个指针将代替传入指针进行相关传入指针的操作;第二个是重载“*”运算符,解引用,返回一个指针所指向的对象;第三个是重载“->”运算符,返回一个指针,对应于上图就是px。

  • 迭代器也是智能指针

在这里插入图片描述

在这里插入图片描述

创建一个list迭代器对象,list::iterator ite;这里的list用于保存Foo对象,也就是list模板定义里的class T,operator*()返回的是一个(*node).data对象,node是link_type类型,然而link_type又是list_node*类型,这里的T是Foo,所以node是list_node类型,所以(node).data得到的是Foo类型的一个对象,而&(operator())最终得到的是该Foo对象的一个地址,即返回Foo 类型的一个指针。

17.仿函数

在这里插入图片描述

每个仿函数都是某个类重载“()”运算符,然后变成了“仿函数”,实质还是一个类,但看起来具有函数的属性。

  • 每个仿函数其实在背后都继承了一个奇怪的类

18.namespace

可以自己定义一个命名空间

19.模板

  • 与类模板不同的是,函数模板在使用是不需要显式地声明传入参数类型,编译器将自动推导类型。

在这里插入图片描述

  • 成员模板在泛型编程里用得较多,为了有更好的可扩展性,以上图为例,T1往往是U1的基类,T2往往是U2的基类,可以看下面这个例子: 通过这种方法,只要传入的U1和U2的父类或者祖类是T1和T2,那么通过这样的方式可以实现继承和多态的巧妙利用,但反之就不行了

20.模板特化

1.必须要先有一个基础的模板

2.使用特换模板函数时格式有要求:
关键字template后面接一对空的尖括号<>
函数名/类名<特化类型>(特化类型 参数1, 特化类型 参数2 , …) 在函数名/类名后跟<>其中写要特化的类型

3.函数形参表必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

在这里插入图片描述

21.模板偏特化

  1. 部分特化(个数上偏):将模板参数类表中的一部分参数特化。

在这里插入图片描述

2.范围上偏,泛化中的T与特化中T*是不一样的,T就是不同的

在这里插入图片描述

22.模板参数

在这里插入图片描述

中第一个错,因为list容器参数有好几个,只在private中模板化一个参数,虽然平常情况其他参数会给默认值,但是在这没有,只有接受一个参数,才不会出错

23.可变数目模板参数

在这里插入图片描述

24.auto

在这里插入图片描述

不能极端使用auto

25.reference

  • reference可以看做是某个被引用变量的别名。
  • object和其reference的大小相同,地址相同(全都是假象)

在这里插入图片描述

在这里插入图片描述

  • reference通常不用声明变量,而是用于参数类型和返回类型描述

  • 光光加&是不能重载

    在这里插入图片描述

前面都一样,在括号后加const算重载,是签名一部分

26.继承和组合都存在时,构造与析构情况

在这里插入图片描述

27.虚指针与虚函数表

定义了三个类,A、B和C,B继承于A,C继承于B,A中有两个虚函数,B中有一个,C中也有一个。编译器将A的对象a在内存中分配如上图所示,只有两个成员变量m_data1和m_data2,与此同时,由于A类有虚函数,编译器将给a对象分配一个空间用于保存虚函数表,这张表维护着该类的虚函数地址(动态绑定),由于A类有两个虚函数,于是a的虚函数表中有两个空间(黄蓝空间)分别指向A::vfunc1()和A::vfunc2();同样的,b是B类的一个对象,由于B类重写了A类的vfunc1()函数,所以B的虚函数表(青色部分)将指向B::vfunc1(),同时B继承了A类的vfunc2(),所以B的虚函数表(蓝色部分)将指向父类A的A::vfunc2()函数;同样的,c是C类的一个对象,由于C类重写了父类的vfunc1()函数,所以C的虚函数表(黄色部分)将指向C::vfunc1(),同时C继承了超类A的vfunc2(),所以B的虚函数表(蓝色部分)将指向A::vfunc2()函数。同时上图也用C语言代码说明了编译器底层是如何调用这些函数的,这便是面向对象继承多态的本质。

在这里插入图片描述

28.this指针

在这里插入图片描述

this指针其实可以认为是指向当前对象内存地址的一个指针,如上图所示,由于基类和子类中有虚函数,this->Serialize()将动态绑定,等价于(*(this->vptr)[n])(this)。

29.const

  • 常量对象只能调用常量成员函数,非常量对象可以调用常量成员函数也可以调用非常量成员函数。

  • 常量成员函数的const和non—const版本同时存在,常量对象只能调用const版本,非常量对象只能调用非常量版本

    在这里插入图片描述

30.重载new和delete以及new[] delete[]

  • 通过重载这两个,可以自己控制内存池

  • 重载后第一个参数必须是size_t

31.重载new() delete()

在这里插入图片描述

想无声无息多分配一些内存时候,可以重载new().

猜你喜欢

转载自blog.csdn.net/m0_53953432/article/details/127835914