高质量C/C++编程读后笔记二

内存管理

640K ought to be enough for everybody

— Bill Gates 1981


内存分配方式

内存分配方式有三种:

(1)       从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

(2)       在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

(3)       从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。


l         【规则7-2-1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

l         【规则7-2-2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

l         【规则7-2-3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

l         【规则7-2-4】动态内存的申请与释放必须配对,防止内存泄漏。

l         【规则7-2-5】用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。


指针和数组的区别

计算内存容量和数组退化
#include <stdio.h>

void Func(char a[12])
{
    printf("%d\n",sizeof(a));
    return;
}

int main()
{
    char a[] = {"12345 67890"};
    printf("%d\n",sizeof(a));//12
    Func(a); //4 退化
    return 1;
}

指针参数传递内存
#include <stdio.h>
#include <stdlib.h>

//错误,p的副本指向新的地址,p不变,且内存泄露
void GetMemory(char* p,int num)
{
    p = (char*)malloc(sizeof(char) * num);
    return;
}

//更改p的地址的值,使得p指向新的申请好的地址
void GetMemory2(char** p,int num)
{
    *p = (char*)malloc(sizeof(char) * num);
    return;
}

//通过函数返回值返回新的地址
char* GetMemory3(int num)
{
    char* p = (char*)malloc(sizeof(char) * num);
    return p;
}

//确保函数返回时指针指向的空间不会被回收
char* GetString()
{
    //p所在的空间在函数返回时会被回收
    char p[] = "hello world";
    return p;
}

//不算错但是没有意义的函数,注意和上一个函数的区别
char* GetString2()
{
    char* p = "hello world";
    return p;
}

int main()
{
    char* test = NULL;
    GetMemory(test,5);
    printf("test = %x\n",test);

    char* test1 = NULL;
    GetMemory2(&test1,5);
    printf("test1 = %x\n",test);

    char* test2 = GetMemory3(5);
    printf("test2 = %x\n",test);

    char* test3 = GetString();
    printf("test3 = %s\n",test);

    char* test4 = GetString2();
    printf("test4 = %s\n",test4);

    return 1;
}

内存耗尽管理

(1)判断指针是否为NULL,如果是则马上用return语句终止本函数。
(2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。
(3)为new和malloc设置异常处理函数。例如Visual C++可以用_set_new_hander函数为new设置用户自己定义的异常处理函数,也可以让malloc享用与new相同的异常处理函数。详细内容请参考C++使用手册。

在用delete释放对象数组时,留意不要丢了符号‘[]’。例如

delete []objects;    // 正确的用法

delete objects;    // 错误的用法


重载,隐藏和覆盖

重载的区分:返回值
编译器根据参数为每个重载函数产生不同的内部标识符

C语言不支持重载,且标识符与C++不同
所以C++调用编译后的C函数,需要用extern C修饰C风格函数
告诉C++编译器,这个符号是C风格的。

隐式转换的二义性
#include <iostream>
using namespace std;

void output( int x)
{
    cout << " output int " << x << endl ;
}

void output( float x)
{
    cout << " output float " << x << endl ;
}



int main()
{
    int   x = 1;
    float y = 1.0;

    //output(0.5);        // error C2668: “output”: 对重载函数的调用不明确
    output(int(0.5));   // output int 0
    output(float(0.5)); // output float 0.5
    return 1;
}

成员函数的重载、覆盖与隐藏

    成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual关键字可有可无。

    覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual关键字。


注意,当成员函数满足覆盖1,2条件但是不满足3,4条件时
会形成隐藏条件。

#include <iostream>

class Base

{

public:

    virtual void f(float x){ cout << "Base::f(float) " << x << endl; }

    void g(float x){ cout << "Base::g(float) " << x << endl; }

    void h(float x){ cout << "Base::h(float) " << x << endl; }

};

class Derived : public Base

{

public:

    virtual void f(float x){ cout << "Derived::f(float) " << x << endl; } //覆盖

    void g(int x){ cout << "Derived::g(int) " << x << endl; } //不满足参数相同 隐藏

    void h(float x){ cout << "Derived::h(float) " << x << endl; } //不满足VIRTULE关键字 隐藏

};

void main(void)

{

    Derived  d;

    Base *pb = &d;

    Derived *pd = &d;

    // Good : behavior depends solely on type of the object

    pb->f(3.14f); // Derived::f(float) 3.14 

    pd->f(3.14f); // Derived::f(float) 3.14



    // Bad : behavior depends on type of the pointer

    pb->g(3.14f); // Base::g(float) 3.14 

    pd->g(3.14f); // Derived::g(int) 3        (surprise!)



    // Bad : behavior depends on type of the pointer

    pb->h(3.14f); // Base::h(float) 3.14      (surprise!)

    pd->h(3.14f); // Derived::h(float) 3.14 

}

参数的缺省值

【规则8-3-1】 参数缺省值只能出现在函数的声明中,而不能出现在定义体中。
【规则8-3-2】如果函数有多个参数,参数只能从后向前挨个儿缺省,否则将导致函数调用语句怪模怪样。

正确的示例如下:

void Foo(int x, int y=0, int z=0);

错误的示例如下:

void Foo(int x=0, int y, int z=0);  


函数错用缺省值导致二义性
#include <stdio.h>

void output(int x);
void output(int x,float y = 0.0);

void output(int x)
{
    printf("x = %d\n",x);
    return;
}

void output(int x,float y)
{
    printf("x = %d,y = %f\n",x,y);
    return;
}

int main()
{
    int x = 1;
    float y = 0.5;

    output(x);//error C2668: “output”: 对重载函数的调用不明确
    output(x,y);
    return 1;
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值