面向对象(C++)

-1 待填坑:运算符重载,模板和泛型编程。文件输入输出

跨文件使用全局变量应该先用extern声明
如果在全局变量和全局函数前面加上static关键字,就无法在其他文件中使用这些变量和函数了。

0、动态内存分配

分配内存:

int* p = new int(4);//注意是一个变量初值为4
int* list = new int[size];//size可以是变量

函数中的局部变量在“栈空间”,但是new出来的是在“堆空间”,是永久的
new 返回的是指针!!!!!!
他们只要不被释放就一直存在,可以在函数中new把这个地址返回出去,会一直存在。
但是如果不返回每一次都new一个p那就会又开一个新的数组。
*的优先级比“.”低,需要加括号,所以有了->,会更快一点。

释放内存

delete p;
delete [] list;//这样才对

1、class

类,构造函数(注意是public)

注意!

如果没有构造函数会缺省无参数构造函数;
如果有其他构造函数,就不会存在无参构造函数了,这可能会导致继承的时候出错(继承的构造函数缺省执行基类无参构造函数)!

and 析构函数

  • 注意创建对象的方式:
Circle a;//如果无参,不能加括号()
Circle a(5);
a = Circle();
a = Circle(5);
  • 避免多次包含:
#ifndef C_H
#define C_H
class Circle
{
public:
...
};

#endif
  • 通常.h中只定义不实现,.cpp中进行实现
  • 实现的时候注意使用
//注意声明可以不写变量名的,就算写了,实现的时候变量名也可以不同的
Circle::getit(int x,int y)
{

}
  • 内联函数 就是inline,编译器会直接把整个复制到调用处,于是小函数会更快因为不需要压入栈调用了

只读成员函数

int getit()const;
表明不会改变class中的数据

静态变量

类中静态变量无需创建对象即可访问
Circle::static_var=1;

拷贝构造函数

看做一种特殊的构造函数,有缺省值,但是是浅拷贝,指针之类的直接赋值过去公用同一个空间。
注意格式:

Course(const Course& course)
{

}

2、继承和多态

继承:

class Circle: public GeometricObject
{

};

访问控制和继承

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。

我们可以根据访问权限总结出不同的访问类型,如下所示:

访问 public protected private
同一个类 yes yes yes
派生类 yes yes no
外部的类 yes no no

一个派生类继承了所有的基类方法,但下列情况除外:

基类的构造函数、析构函数和拷贝构造函数。
基类的重载运算符。
基类的友元函数。

继承类型

当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。

我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

泛型程序设计:当程序需要基类对象的时候,可以提供派生类对象

构造函数和析构函数:

调用基类构造函数:

Circle(int sb) : BaseClass(sb)//后面可以用前面传入的参数
{

}

即使没有写后面那个,缺省调用无参构造函数
构造函数链:从基类开始执行
析构函数链:从派生类开始执行
P496:拷贝构造函数的继承

函数重定义

定义一个和基类函数签名相同且返回值相同的函数就可以覆盖基类的。
如果还是想要调用基类的函数,应该使用:
Circle.Fatherclss::toString();

函数重载

签名不同名字相同

签名是什么鬼

在C++中,"签名"指的是函数或方法的唯一标识符,它由函数的名称、参数类型和参数顺序组成。函数签名用于区分不同的函数重载,即在同一作用域内有多个函数具有相同名称但不同参数列表的情况。

函数签名的组成部分包括:

函数名称:这是函数的标识符,用于唯一标识函数的名称。

参数类型:这是函数参数的类型列表,包括参数的数据类型和顺序。参数类型的不同组合会导致不同的函数签名。

参数顺序:参数的顺序也是函数签名的一部分。如果两个函数有相同的参数类型,但参数顺序不同,它们仍然被视为不同的函数。

下面是一个简单的示例来说明函数签名的概念:


// 函数签名示例
int add(int x, int y); // 函数签名为 "add(int, int)"
float add(float x, float y); // 函数签名为 "add(float, float)"
int subtract(int x, int y); // 函数签名为 "subtract(int, int)"

// 重载函数
int calculate(int x, int y); // 函数签名为 "calculate(int, int)"
int calculate(int x, int y, int z); // 函数签名为 "calculate(int, int, int)"

在上面的示例中,有多个函数重载,它们具有不同的函数签名,因为它们的参数类型和/或参数数量不同。函数签名的不同之处在于函数的名称、参数类型和参数顺序,这使得编译器能够区分这些不同的函数,并正确地调用相应的函数。

签名相同,返回值只有一种,不然肯定不能区分了。

函数签名的理解对于函数重载、模板编程和函数指针等C++编程的概念非常重要。


多态

可以用基类指针或引用来引用派生类对象,和泛型编程其实差不多。
形参可以是基类的,但是可以传入派生类的实参。

虚函数和动态绑定

虚函数:在运行时根据目前对象的实际类型选择调用哪一个函数。
在基类中设置为virtual即可,继承的不需要声明为virtual因为默认均为virtual了
例如形参为基类传入派生类的时候,没有virtual调用函数时调用基类的,用了virtual的函数就会调用派生类的。
这个功能叫做(动态绑定)

需要注意:
1、传入的时候必须使用引用或者指针,否则无法动态绑定
2、会重定义的函数应该声明为虚函数,否则调用可能达不到想要的效果。但是如果不需要重定义,那就不要声明为虚函数,因为动态绑定消耗更多时间和系统资源
3、静态绑定:例如有不同的签名的函数的匹配。这是由于函数的性质决定的,但是动态绑定是由于对象的性质决定选择哪一个函数、
4、含有虚函数的类称为多态类型
5、虚函数可以要求派生类必须实现这些函数(其实c++并不能)

抽象类和纯虚函数

  • 抽象类不能创建对象,其中只有抽象函数,他们在具体的派生类中实现。
  • 抽象函数:不能在基类中实现的函数(如GET_TYPE)
  • C++中,抽象函数称为纯虚函数
  • 包含纯虚函数的类就称为抽象类

定义纯虚函数:(真tm抽象)

virtual double getarea() = 0;
virtual double readonly()const = 0;

类型转换: static_cast 和 dynamic_cast

引入:如果形参是基类,在这个函数中我们无法调用子类的函数和其他成员,不好操作

办法1:static_cast<Circle*>(p)->getRadius()//<>内部是转换成的类型,p必须是指针
注意到,这里是强制转换,有可能本来就不是圆的(如矩形)也可以强制转换成圆并造成错误

办法2:dynamic_cast<Circle*>(p)->getRadius()//<>内部是转换成的类型,p必须是指针
如果强制转换错误新指针将会变成NULL,NULL在一些标准库如iostream和cstddef中都有定义


向上转型:派生类->基类 可以直接隐式转换(直接赋值之类都可以)

向下转型:基类->派生类 :利用cast,必须显式转换

posted @ 2023-10-28 15:00  lei_yu  阅读(4)  评论(0编辑  收藏  举报