您的位置:首页 > 编程语言 > C语言/C++

Boolan C++面向对象编程(下) 课程笔记Week2

2018-02-04 23:57 302 查看
一、上周作业总结1、定义一个空类,其中不包含任何信息,则其sizeof值为1,因为当我们声明该类的时候,它必须在内存中占用一定的空间,否则无法使用这些实例。
2、当类里面存在虚函数时,这个类所占的内存就会比没有虚函数时候大一点,大的位置是在类的成员变量前面会多出一个指针(vptr),它指向虚指针表(vtbl),虚指针表里面的每一个指针再指向对应的虚函数。从而实现动态绑定; 二、知识梳理
1. 对象模型(objectmodel):关于vptr和vtbl
注意:
1)C++编译器一旦发现类中有虚函数,就会为该类生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针(即从内存角度上看,含有虚函数的对象里头会多一个指针)。在32为机器上,一个指针占4字节的内存空间,所以sizeof值为4,。在64位机器上,一个指针占8字节内存,所以sizeof值为8。
2)虚函数表放的是指针(地址)。虚指针关联虚函数,和一般函数无关。
3)C是以静态绑定的方式调用函数的。而C++是以动态绑定的方式调用函数的,C++调用虚函数若以C的风格来写,应该为:
(*(p->vptr)
)(p); // 或 (* p->vptr
)(p); 4)继承的是函数调用权,父类有虚函数,则子类一定有虚函数。例子:
class A{
public:
virtual void vfunc1();
virtual void vfunc2();
void func1();
void func();
private:
int m_data1, m_data2;
};

class B:public A {
public:
virtual void vfunc1();
void func2();
private:
int m_data3;
};

class C:public B{
public:
virtual void vfunc1();
void func2();
private:
int m_data1, m_data4;
};

5)前面提到C++绑定函数有两个做法,一个是静态绑定call。一个是动态绑定,动态绑定的三个条件是:通过指针调用;向上转型;调用的是虚函数。
6)声明一个容器,里面放指针,指向父类(声明为指向父类的指针,但绑定的(new)是子类)。当子类都有修改某个虚函数,通过遍历容器对象,调用这个虚函数,就能依次调用各自的版本,如下图。(再C里面实现这个行为靠if else else …)。



    虚函数的这种做法就是“多态”,同样是point toA,但是却实际上指向不同的东西。下面的例子可以解释这个行为(注意这里的this pointer。简单的说,通过对
4000
象来调用一个函数,那个对象的地址就是this pointer。所有成员函数都有隐藏参数this
7)关于动态绑定(Dynamic Binding):
(*(p->vptr)
)(p);   // 或 (* p->vptr
)(p);

意思是:通过指针找到它的虚指针,再找到它的虚函数表,取出第n个,把它当成函数指针去调用。由于是通过p去调用,因而p是this pointer。(这句话也解释了上面的多态行为)
2.谈谈const  
const member functions (常量成员函数)
当对象调用成员函数的时候,对象可能是const也可能不是const,成员函数可能是const也可能不是const,这样就存在两种情况。
规则:当成员函数的const和non-const版本同时存在,const object只会(只能)调用const版本,non-const object只会(只能)调用non-const版本。(而当只有一者存在时,const object也只能调用const的版本。但是non-const object却可能调用const 或non-const 版本。)
    下面这个例子是设计的比较不好的:
const String str("hello world");
str.print();
// 如果当初设计 Srting::print()时未指明 const,
// 那么就是由 const object调用 non-const member function,会出错。
3.关于new,delete
0)在C++中,设计size_t就是为了适应多个平台的。size_t的引入增强了程序在不同平台上的可移植性。

1 ) C++中的各种new:
    (1)new T:调用类的operator new(若重载了)或者全局的operator new 分配空间,然后用类型后面列的参数来调用构造函数。通过调用delete来删除。
new TypeName(initial_args_list);

//例子
int* p = new int;
int* p = new int(10);
int* p = new foo("hello");

//调用delete来删除:
delete p;    (2)new T[]:这种new用来创建一个动态的对象数组,它会调用对象的operator new[]来分配内存,然后调用对象的默认构造函数初始化每个对象。
new TypeName[num_of_objects];

//例子
int* p= new int[10];

//销毁时用operator delete[];     ( 3 ) new() T和new() T[]
           带参数的new,会调用operator new(size_t, OtherType)来分配内存。这种语法通常用来在某个特定的地址构建对象,称为placement new,前提是operator new(size_t, void*)已经定义,通常编译器已经提供了一个实现,包含头文件即可,这个实现只是简单地把参数地指定地地址返回,因而new()运算符就会在括号里的地址上创建对象。
          第二个参数不一定要是void*。我们可以提供自己的operator new(size_,Type),来决定new的行为,例如:
char data[1000][sizeof(foo)];

inline void* operator new(size_t, int n)
{
return data
;
}

// 把对象创建在data的第六个单元上
foo* p = new(6) foo();
// foo还有bar都是无意义的代名词,就好像中文里面的“某某“,“张三李四”差不多。
    (4)operator new(size_t)
             这个运算符分配参数指定大小的内存并返回首地址,可以为自定义的类重载这个运算符,方法就是在类的声明中重载全局::operator new。(我的理解:operator new 一般不显示调用,而是在调用new时,编译器转化后隐式调用)
void* operator new(size_t size)
{
//在这里分配内存并返回其地址(功能和全局operator new一致)
}
            无论声明与否,类里面重载的各种operator new 和operator delete都具有static属性,一般不需直接调用operator new,除非直接分配原始内存(类似于C的malloc)。在冲突的情况下要调用全局的o'perator加上::作用域运算符。
::operator new(1000);//分配1000个字节。返回的内存需要回收的话,调用相应的operator delete
    (5)operator new[](size_t)
             同样是分配内存、返回地址,但是专门针对数组,也就是new T[]这种形式,需要时可以显示调用。

    (6 ) operator new(size_t size, OtherType other_value)和operator new[](size_t size, OtherType other_value)
            placement new,参见上面的new()。需要强调的是,new用来创建对象并分配内存,它的行为是不可改变的,可以改变的是各种operator new,我们可以通过重载operator new 来实现我们的内存分配方案。
2)new和delete是表达式,可以分解进去(编译器转化)。
3)重载::operator new, ::operatordelete, ::operator new[], ::operator delete[]
    即重载全局函数。没有重载就用全局的。注意delete要接受指针。
4)重载member operator new/delete
接管了内存分配要做什么?做一个内存池。
5)重载member operator new[]/delete[]
接管使用者的行为



如果使用者想绕过设计者的设计,可以加全局的作用域。(写上global scope operator :: 会绕过前述所有overloaded functions,强迫使用global version)
5)重载new(), delete()
placement new:
我们可以重载class member operatornew(), 写出多个版本,前提是每一版本的声明都必须有独特的参数列,其中第一参数必须是size_t,其余参数以new所指定的placemnet arguments 为初值。出现于new (……) 小括号内的便是所谓的placement arguments。
我们也可以重载class member operatordelete() (或称此为placement operator delete),写出多个版本。但他们绝不会被delete调用。只有当new所调用的ctor抛出exception,才会调用这些重载版的operatordelete()。它只可能这样被调用,主要用来归还未能完全创建成功的object所占用的memory。
参考:https://www.jianshu.com/p/4af119c44086
示例:





这些对应的delete被调用的时机只有一个,那就是对应的new分配内存之后,如果构造函数发挥异常(表示构造对象时失败),那就把刚刚分配的内存释放掉。
6)basic_string 使用new(extra)扩充申请量
(这部分不太理解,后面补上。)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: