第七章-第十章

1. 指针和引用的区别

引用初始化时被指定对象,以后不能改变。

//出错指针没有初始化
int *temp;
*temp = 1;
int *p, *q;
int a[3];
p = a;
q = &a[2];
//q-p = 2而不是2*sizeof(int)
class A
{
    public:
        A() {ma = 1; mb = 2;}
        ~A(){};
        void f(){printf("%d%d", ma, mb);}
    private:
        int ma;
        int mb;       
};
class B
{
    public:
        A() {mc = 3;}
        ~A(){};
        void f(){printf("%d", mc);}
    private:
        int mc;
};

void main()
{
    A a;
    B *p = (B*)(&a);
    p->f();
}
//输出1, printf输出的变量对应其在类中的偏移

2. 函数需要在其被调用之前声明,跟main()函数无关。

3. 函数指针

float (**a)[10]; //定义一个二级指针,它指向一个一维数组的指针,数组每个元素都是float
double *(*b)[10]; //定义一个指针,指向一个一维数组,数组每个元素都是double*
double (*f[10])(); //f是一个数组, 每个元素是一个函数指针
int (*(*F)(int, int))(int); //F是一个函数的指针,返回值也是一个函数指针,该返回的指针有一个int参数, 返回值为int

4. C++有malloc/free,为什么 还需要new/delete?

malloc/free是C/C++的标准库函数,new/delete是 运算符。对于非内部数据类型(自定义类)来说,malloc/free无法满足动态 对象的要求。标准库函数不在编译器控制权限之内,不能把构造函数和析构函数的任务交于malloc/free。new能能完成动态内存分配和初始化工作。delete完成内存清理和释放工作。

5. 指针和句柄

Windows是一个以虚拟内存为基础的操作系统。不允许程序直接通过物理地址访问对象。程序将想访问的对象的句柄传给系统,系统根据句柄检索维护的对象列表就能知道想访问的对象及其物理地址。

6. 智能指针

注:auto_ptr放入container是不安全的。

如果出现异常下面函数f()没有执行delete会导致内存泄漏。

void f()
{
    T* pt(new T);
    delete pt;
}

解决方法:用一个"灵巧"的类指针对象包含这个指针,其析构时自动删除此指针。(发生异常时对象的析构函数会自动调用)

void f()
{
    auto_ptr<T> pt(new T);
    .....
}

7. this指针

this指针指向实例本身,this 只能在成员函数中使用,全局 函数、静态函数都不能使用this。实际成员函数默认第一个参数为

T* const this。this在成员函数开始 之前构造,在成员函数结束后清除。this相当于非静态成员 函数的一个隐式参数, 不占用对象空间。

A a;
a.func(10);

//编译器将会编译成 A::fun(&a, 10);

8. 泛型编程

泛型编程是一种基于发现高效算法的最抽象表示的编程方法。

template<typename T>
const T* My_friend(T *array, T n, T x)
{
    const T* p = array;
    ......
    
}


template<class T>
class Operate{

    Public:
        static T Add(T a, T b)
        {}
    ......
}
int x, A=1, B=2;
x = Operate<int>::Add()

9. 面向对象

面向对象的三个原则:封装(private), 继承(interface), 多态(virtual)。

对于一个空类,编译器默认产生4个成员函数:默认构造函数,析构函数, 赋值构造函数, 赋值函数。

C++ 中struct 和 class意义一样,只是struct默认public, class默认private。

初始化列表初始化变量的顺序根据成员变量声明顺序来执行。

常量必须在构造函数的初始化列表里初始化。

10. 构造函数和析构函数

 无论析构函数是不是虚函数,派生类对象被撤销时,肯定会依次调用基类的析构函数。

为什么要搞一个虚的构造函数存在?因为多态

Cbase *pbase;
Cchild c;
pbase = &c;
//在撤销指针pbase时会调用Cbase的析构函数,但是虚构造函数就可以调用子类的析构函数。

虚函数是 有代价的》每个虚函数的对象都必须 维护一个v表。

析构函数可以是内联函数。

class B {
    B(int i):data(i){}
    ...
}
B play(B b)
{
    ...
}

int main()
{
    B temp = Play(5);
    ...
}
//单个参数的构造函数如果不添加explicit关键字,会定义一个隐含的类型转换(从参数转换到自己)
//第一次构造函数对于5,temp的构造函数调用的是编译器生成的赋值构造函数而不是默认构造函数。
//B temp = Play(5);第一次是对函数参数参数进行构造和析构,第二次是函数返回类对象时,再次调用赋值构造函数来创建对象副本。
//所以还需要一次析构函数来析构这个副本(而不是析构temp)。B temp = Play(5)析构情况等同于 Play(5);
//因为编译器把B temp = Play(5)中temp的析构和返回副本的析构合到一起以提升效率。

11. 复制构造函数

所有需要分配系统资源的用户自定义类型都需要一个赋值构造函数。



//复制构造函数
MyString s1("hello");
MyString s2 = s1;

String::String(const String &other)
{
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data, other.m_data);
}

//赋值函数
MyString s1(hello);
MyString s2;
s2 = s1;

String & String::operate =(const String &other)
{
    if(this == &other)
        return *this;
    delete [] m_data;
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data, other.m_data);
    return *this;
}

12. 虚函数

virtual Base* Base::copy(Base* );

virtual Derived* Derived::copy(Base*);
//返回类型可以是父类指针或子类指针(引用),参数类型必须一致 

13. 异常

当对象部分构造,发生异常,已经构造完毕的的子对象会逆序被析构。所以析构函数中最好不要再抛出异常除非自己捕获(吃掉)。

猜你喜欢

转载自blog.csdn.net/qq_23084801/article/details/81156240