网教网

搜索
查看: 81|回复: 0

C++面向对象高级编程

[复制链接]

2

主题

3

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2023-4-10 07:38:18 | 显示全部楼层 |阅读模式
本文是我学习C++面向对象的一些笔记,主要来源于侯捷老师在geekband的讲课视频。
Object Based vs. Object Oriented(面向对象 vs.基于对象)
基于对象是单一的class的设计
面向对象是多重classes的设计,class和class之间的关系
C++代码的基本形式


用自己编写的头文件:#include“complex.h”
用系统的标准库的头文件:#include<iostream.h>
下面来介绍头文件的标准布局:
Header布局

#ifndef __COMPLEX__
#define __COMPLEX__

......


#endif
这表示如果没有定义COMPLEX才去定义COMPLEX,表示在执行到当前头文件的时候如果已经存在COMPLEX就不再执行此代码的文件,这叫做guard声明。
在这个文件中又分类三个部分:
forward声明
声明有那些类,以及那些全局函数;
class声明
声明类中的一些较为复杂的成员函数,对于简单的函数最好直接在类中定义,定义在类内完成自动成为inline的候选,即使在内外声明某个类内函数是inline的,具体是否是inline的也是由编译器决定的。
class下的定义
定义一些对于class下的函数。
如下图:


Acess level(访问级别)

也就是访问权限,私有成员无法在内外访问,友元除外。
若构造函数为私有成员,相当于此类无法被构造。例如Singleton设计模式


const menber function

若某个成员函数不会改变成员属性,最好都把他写出常成员函数的形式。这是因为若创建一个常对象的时候,我们就没法调用不是常成员函数的成员函数了。(例如若在非 常成员函数内部修改了成员变量的值,但是常对象又不允许修改,这就出现了矛盾)


value and reference

pass by value vs. pass by reference
在没有特殊要求的情况下,引用传递是要优于值传递的,但是要注意到应用传递的值改变,原来的数据也会改变,为了防止原来的数据改变会在传递前加一个const。
return by value vs. return by reference
返回引用的前提是在函数内部返回的参数不是一个局部参数。
friend

作为友元的函数能够访问类里面的私有成员,相同的类的各个对象互为友元


operator overloading

分为成员函数和全局函数的操作符重载,成员函数的操作符重载,里面的参数自带this对象。


但是虽然自带this对象,我们并不能将他写出来,写出来后编译器反而会报错。
一般情况下考虑是用成员函数还是非成员函数取决于操作符的作用后是不是对称的,若是对称的则都可以采用。
记住一点,操作符是作用在左边的变量上面,例如


<<这个操作符只能定义为全局函数。
Classes有两个经典的分类,一个是成员变量不带指针的,如上面的complex类,另外一个是成员变量带指针的,如下面的string类;
Big Three

拷贝构造函数,拷贝赋值函数,以及析构函数。编译器会默认提供给我们三个这样的函数,当我们创建一个类对象,程序运行的时候会自动调用这个类的构造函数函数,当程序运行结束的时候,会调用析构函数。
现在有这样一个问题:
有一个string类,里面有一个成员变量为char*,这是一个指针,我们创建一个string对象 s,将s的值通过拷贝构造或者拷贝赋值的方式给s1,那么s1当然拥有于s相同的成员变量,其类型也为char*,当程序运行结束的时候,我们会通过s和s1的析构函数释放s于s1的数据,但是由于s中的数据是一个指针,当执行s的析构函数的时候,会将s的成员变量 char*指向的数据删除,执行s1的析构函数也会删除char*指向的数据,这样就造成了内存的重复释放。为了解决这个问题,我们需要在string类的构造函数和析构函数中手动开辟和手动释放。


以上是普通的构造函数,因为是指针,所以我们需要开辟数据让这个指针指向为我们开辟的数据,并且需要手动释放。


这是我们提供的拷贝构造函数,先是开辟了比str.date数据大一的数组(因为默字符的最后一位为0),然后再家用c的strcpy将str.date的数据赋值到我们开辟的数组中,这样就解决了上述的问题。


这是提供的拷贝赋值函数,与拷贝构造类似,不同之处在我们要释放待赋值对象中的数据。
这里检测自我赋值的是必须的,赋值对象与被赋值对象相同,我们是首先释放被赋值对象中的数据,但是我们已经将其释放,这相当于释放的赋值对象,所以我们没办法成功的赋值。
stack和heap

stack是存在于某作用域的一块内存空间,在function body内声明的任何变量,都属于function的作用域内。
heap是由操作系统提供的一块全局内存空间,由程序员手动分配和释放。
stack的生命期:


static local objects的生命期


global objects的生命期


heap objects的生命期


new关键字

new是先分配内存,在调用构造函数。


delete关键字

是先调用构造函数,在释放内存


注意:即时内中的成员没有指针时,也建议array new 搭配array delete;
stactic关键字

类中static分为static变量和static函数。
类中的non static函数通过this指针区分时那个对象调用这个函数,但是类中的static变量和函数是所有对象共享的。
static变量在类内声明,类外定义。
namespace



OOP和OOD

OOP是指object oriented programing,OOD是指object Oriented Design。
分为继承(Inheritance),复合(Composition),委托(Delegation)。
Composition表示对象A中有对象B,可以调用B中的成员函数实现A中成员函数的功能。
构造和析构过程如下:


Delegation是指以类A中有类B的指针,相当于就是这些A对象共用一个类B对象(A中的B的指针相同);
继承是指子类拥有父类的所有性质;


virtual functions

类中的函数分类:
non-virtual:不希望子类重新定的函数(no-override).
virtual:希望之类重新定义,并且对它已经有默认的定义。
pure virtual:希望子类(derived class)重新定义,没有默认定义;
class Shape
{
public:
    int objectID()const;//non-virtual
    virtual void draw() const =0;//pure virtual
    virtual void error(const std::string& masg);//virtual
   
}通过子类调用父类中的虚函数是调用的子类中override(重新定义的函数),因为传进去的是子类的对象,当然调用的是子类的函数。
Inheritance+Composition的构造和析构



上图中的构造和析构顺序是:
第一个问号出的构造顺序是:Base->Compoent->Derived,析构顺序相反
第二个问号的构造顺序是:Compoent->Base->Derived,析构顺序相反
Delegation+Inheritance

OOD面向设计编程,下面是一些设计模式。
Observer



上述代码的Delegation体现在vector<Observer*>中,若没有Observe的派生类,这vector中的所有observe都一样,若有Obeserve的派生类,则根据派生类中的overide的函数不同,我们在Subject中调用这些函数时的效果也不同。
Composite



我们想要让子类Composite的vector<>中既有子类Primitive的对象,又有Composite自己,所以我们让vector中的元素是父类的指针Composite*,里面的元素是指向父类对象的指针,我们就可以让父类的指针指向子类的对象。
Prototype



没看太懂,之后补0.0,大致是想通过父类直接调用子类,但是子类具体是啥不清楚。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表