Top

CSDSTDCPP01 DAY02

  1. 布尔类型
  2. 重载关系
  3. C形式的调用规格
  4. 缺省参数
  5. new和delete

1 布尔类型

1.1 问题

布尔类型是表示布尔量的数据类型,其值只有真(true)和假(false)两个值,布尔类型的数据占1个字节的存储空间。

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:布尔类型

代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

bool b;
    b = true;
    b = false;
std::cout<<std::boolalpha<< b <<std::endl;

std::cout<<sizeof(bool) <<std::endl;

    b = 5;
    b = 5.5;
    b = 'a';
std::cout<<std::boolalpha<< b <<std::endl;

return0;
}

上述代码中,以下代码:

bool b;
    b = true;
    b = false;

定义了布尔类型变量b,并将其赋值为true和false。

上述代码中,以下代码:

std::cout<<sizeof(bool) <<std::endl;

从运行结果可以看出,布尔类型占1个字节的存储空间。

上述代码中,以下代码:

    b = 5;
    b = 5.5;
    b = 'a';

表示任何基本数据类型的数据均可直接复制给布尔变量b。

1.3 完整代码

本案例中的完整代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

bool b;
    b = true;
    b = false;
std::cout<<std::boolalpha<< b <<std::endl;

std::cout<<sizeof(bool) <<std::endl;

    b = 5;
    b = 5.5;
    b = 'a';
std::cout<<std::boolalpha<< b <<std::endl;

return0;
}

2 重载关系

2.1 问题

同一作用域内,一组具有相同函数名,不同参数列表的函数,构成重载关系,这组名称相同的函数成为重载函数。重载函数通常完成的功能相近,这样做的好处是减少了函数名的数量,提供了程序的可读性。

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:重载关系

代码如下:

#include <iostream>

void print(void)
{
std::cout<<"call print function"<<std::endl;
}

void print(int n)
{
std::cout<<"n = "<< n <<std::endl;
}

void print(double d)
{
std::cout<<"d = "<< d <<std::endl;
}

void print(int* p)
{
std::cout<<"*p = "<< *p <<std::endl;
}

int main(int argc, constchar * argv[])
{

print();
print(10);
print(10.32);

int n = 100;
print(&n);

return0;
}

上述代码中,以下代码:

void print(void)
{
std::cout<<"call print function"<<std::endl;
}

void print(int n)
{
std::cout<<"n = "<< n <<std::endl;
}

void print(double d)
{
std::cout<<"d = "<< d <<std::endl;
}

void print(int* p)
{
std::cout<<"*p = "<< *p <<std::endl;
}

定义了一组函数名为print的函数,但这些函数的参数均不相同,这就使它们构成了重载关系,相互称为重载函数。虽然只有一个函数名,但却是四个不同的函数。

注意:参数相同仅返回值不同的两个函数不构成重载关系。

代码如下所示:

void print(void)
{
std::cout<<"call print function"<<std::endl;
}

int print(void)
{
std::cout<<"call print function"<<std::endl;
return1;
}

上述代码将报错,print函数重定义。

步骤二:同一作用域内的函数才构成重载关系

代码如下:

void foo (void)
{
}
namespace ns1 {
void foo (int a)
    {
    }
namespace ns2 {
void foo (int a, int b)
        {
        }
void bar (void)
        {
foo (10, 20);
        }
    }
void hum (void)
    {
foo (30);
    }
}

void fun (void)
{
foo ();
}

上述代码中有三个同名的foo函数,它们的参数也不相同,但他们不构成重载关系,因为他们分属三个不同的名字空间,在不同的作用域中。

2.3 完整代码

本案例的完整代码如下所示:

#include <iostream>

void print(void)
{
std::cout<<"call print function"<<std::endl;
}

void print(int n)
{
std::cout<<"n = "<< n <<std::endl;
}

void print(double d)
{
std::cout<<"d = "<< d <<std::endl;
}

void print(int* p)
{
std::cout<<"*p = "<< *p <<std::endl;
}

void foo (void)
{
}
namespace ns1 {
void foo (int a)
    {
    }
namespace ns2 {
void foo (int a, int b)
        {
        }
void bar (void)
        {
foo (10, 20);
        }
    }
void hum (void)
    {
foo (30);
    }
}

void fun (void)
{
foo ();
}

int main(int argc, constchar * argv[])
{

print();
print(10);
print(10.32);

int n = 100;
print(&n);

return0;
}

3 C形式的调用规格

3.1 问题

重载是通过C++换名实现的,换名机制限制了C和C++模块之间的交叉引用。为了能够让C++编译器按照C方式处理函数接口,可以使用extern关键字实现。

3.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:C形式的调用规格

代码如下所示:

extern"C"
{
void printChar(char c)
    {
std::cout<<"c = "<< c <<std::endl;
    }

void printInt(int n)
    {
std::cout<<"n = "<< n <<std::endl;
    }
}

int main(int argc, constchar * argv[])
{

printChar('a');
printInt(10);

return0;
}

上述代码中,以下代码:

extern"C"
{
void printChar(char c)
    {
std::cout<<"c = "<< c <<std::endl;
    }

void printInt(int n)
    {
std::cout<<"n = "<< n <<std::endl;
    }
}

表示要求C++编译器按照C方式处理函数接口,即不做换名。由于无法执行换名操作,当然也就无法重载,所以需要用不同的函数名区分相似功能函数。

换名操作实际上就是在编译时,将匹配好的重载函数重新命名的方法。实际上,在编译后重载函数的函数名还是不相同的。

3.3 完整代码

本案例的完整代码如下所示:

extern"C"
{
void printChar(char c)
    {
std::cout<<"c = "<< c <<std::endl;
    }

void printInt(int n)
    {
std::cout<<"n = "<< n <<std::endl;
    }
}

int main(int argc, constchar * argv[])
{

printChar('a');
printInt(10);

return0;
}

4 缺省参数

4.1 问题

函数的缺省参数是指在定义函数的时候,给函数形参一个缺省值,当调用该函数的时后,若未给出实参,则与该实参相对应的形参取缺省值。函数的缺省参数是在编译阶段解决的,因此只能用常量、常量表达式或者全局变量等非局部化数值作为缺省值。

4.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:函数的缺省参数

代码如下所示:

#include <iostream>

void foo(int a, int b = 456)
{
std::cout<<"a = "<< a <<std::endl;
std::cout<<"b = "<< b <<std::endl;
}

int main(int argc, constchar * argv[])
{

foo(100, 200);
foo(300);

return0;
}

上述代码中,以下代码:

void foo(int a, int b = 456)
{
std::cout<<"a = "<< a <<std::endl;
std::cout<<"b = "<< b <<std::endl;
}

定义了一个函数foo,该函数有两个形参,第二个形参的后面有一个数值456,它就是缺省参数。

注意:

缺省参数只能是常量、常量表达式或者全局变量,如下代码是错误的:

void foo(int a,int b = a);

带缺省参数的形参b被定义成局部变量形参a的值了。

上述代码中,以下代码:

foo(100, 200);

在调用函数foo时,对于带缺省参数的形参位置可以像以前一样定义实参,这样,缺省参数将变得无效,实参200被赋值给形参。

上述代码中,以下代码:

foo(300);

在调用函数foo时,对于带缺省参数的形参位置也可以不写任何实参值,这样,缺省参数将起作用,相当于把实参456被赋值给形参。

4.3 完整代码

本案例的完整代码如下所示:

#include <iostream>

void foo(int a, int b = 456)
{
std::cout<<"a = "<< a <<std::endl;
std::cout<<"b = "<< b <<std::endl;
}

int main(int argc, constchar * argv[])
{

foo(100, 200);
foo(300);

return0;
}

5 new和delete

5.1 问题

new和delete是C++提供的两个运算符,分别用于动态内存的分配和释放。new相当于C语言中的malloc函数,delete相当于C语言中的free函数。

5.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:动态内存分配

代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

int *p = newint;
    *p = 10;
std::cout<< *p <<std::endl;
delete p;

    p = newint(100);
std::cout<< *p <<std::endl;
delete p;

return0;
}

上述代码中,以下代码:

int *p = newint;

为整型指针变量p分配4个字节的存储空间。

上述代码中,以下代码:

delete p;

将指针p所指向的存储空间释放掉。

上述代码中,以下代码:

    p = newint(100);

为整型指针变量p分配4个字节的存储空间,同时将新分配的存储空间初始化为整数100。

步骤二:不能与C语言中的动态内存分配函数混用

代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

int *p = newint;
    *p = 10;
std::cout<< *p <<std::endl;
delete p;

    p = newint(100);
std::cout<< *p <<std::endl;
delete p;

    p = newint;
free(p);

    p = (int*)malloc(sizeof(int));
delete p;

return0;
}

上述代码中,以下代码:

    p = newint;
free(p);

    p = (int*)malloc(sizeof(int));
delete p;

是不建议使用的方法,它会带来不可预知的问题。

步骤三:数组的分配与释放

代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

int *p = newint;
    *p = 10;
std::cout<< *p <<std::endl;
delete p;

    p = newint(100);
std::cout<< *p <<std::endl;
delete p;

    p = newint;
free(p);

    p = (int*)malloc(sizeof(int));
delete p;

    p = newint [4] {10, 20, 30, 40};
for (int i = 0; i <4; i++)
std::cout<< p[i] <<' ';
std::cout<<std::endl;
delete[] p;

int (*row) [4] = newint [3][4];
row[1][2] = 15;
delete[] row;

return0;
}

上述代码中,以下代码:

    p = newint [4] {10, 20, 30, 40};

为整型指针变量p分配4个整型变量所占的存储空间,即4*4 = 16个字节。这样相当于分配了一个整型数组。用new操作符动态分配数组时,会在数组首元素前面多分配4或8个字节,用以存放数组的长度。

上述代码中,以下代码:

delete[] p;

将分配的16个字节的存储空间释放。注意此时一定不要忘记中括号[]。

上述代码中,以下代码:

int (*row) [4] = newint [3][4];

可以分配多维数组,但需要注意的是,高维数组释放时,依然使用如下语句:

delete[] row;

步骤四:野指针和空指针

代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

int *p = newint;
    *p = 10;
std::cout<< *p <<std::endl;
delete p;

    p = newint(100);
std::cout<< *p <<std::endl;
delete p;

    p = newint;
free(p);

    p = (int*)malloc(sizeof(int));
delete p;

    p = newint [4] {10, 20, 30, 40};
for (int i = 0; i <4; i++)
std::cout<< p[i] <<' ';
std::cout<<std::endl;
delete[] p;

int (*row) [4] = newint [3][4];
row[1][2] = 15;
delete[] row;

int *p1 = newint;
    *p1 = 200;
delete p1;
delete p1;

int *p2;
delete p2;

int *p3 = NULL;
delete p3;

return0;
}

上述代码中,以下代码:

int *p1 = newint;
    *p1 = 200;
delete p1;
delete p1;

的最后一行,重复释放p1。因为倒数第二行释放后,p1已经变成野指针,释放野指针会导致程序崩溃。

上述代码中,以下代码:

int *p2;
delete p2;

指针p2定义时没有初始化,p2也是野指针。

上述代码中,以下代码:

int *p3 = NULL;
delete p3;

不会崩溃,因为delete运算符对空指针不作任何操作。

步骤五:内存分配失败

代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

int *p = newint;
    *p = 10;
std::cout<< *p <<std::endl;
delete p;

    p = newint(100);
std::cout<< *p <<std::endl;
delete p;

    p = newint;
free(p);

    p = (int*)malloc(sizeof(int));
delete p;

    p = newint [4] {10, 20, 30, 40};
for (int i = 0; i <4; i++)
std::cout<< p[i] <<' ';
std::cout<<std::endl;
delete[] p;

int (*row) [4] = newint [3][4];
row[1][2] = 15;
delete[] row;

int *p1 = newint;
    *p1 = 200;
delete p1;
//delete p1;

int *p2;
//delete p2;

int *p3 = NULL;
delete p3;

try {
char* p = newchar[0xFFFFFFFF];
p[0] = 'a';
delete p;
    }
catch (std::bad_alloc& ex) {
std::cerr<<"内存分配失败!"<< std::endl;
exit (-1);
    }

return0;
}

上述代码中,以下代码:

try {
char* p = newchar[0xFFFFFFFF];
    }
catch (std::bad_alloc& ex) {
std::cerr<<"内存分配失败!"<< std::endl;
exit (-1);
    }

当使用new分配内存失败时,将抛出异常。

步骤六:定位分配

代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

int *p = newint;
    *p = 10;
std::cout<< *p <<std::endl;
delete p;

    p = newint(100);
std::cout<< *p <<std::endl;
delete p;

    p = newint;
free(p);

    p = (int*)malloc(sizeof(int));
delete p;

    p = newint [4] {10, 20, 30, 40};
for (int i = 0; i <4; i++)
std::cout<< p[i] <<' ';
std::cout<<std::endl;
delete[] p;

int (*row) [4] = newint [3][4];
row[1][2] = 15;
delete[] row;

int *p1 = newint;
    *p1 = 200;
delete p1;
//delete p1;

int *p2;
//delete p2;

int *p3 = NULL;
delete p3;

try {
char* p = newchar[0xFFFFFFFF];
p[0] = 'a';
delete p;
    }
catch (std::bad_alloc& ex) {
std::cerr<<"内存分配失败!"<< std::endl;
exit (-1);
    }

char* pool = newchar[1024]; // 分配内存池
int* pn = new (pool) int (123);
char* ps = new (pool + 4) char[15];
strcpy (ps, "Hello, World !");
double* pd = new (pool + 19) double (3.14);
std::cout<< *pn <<std::endl; // 123
std::cout<< ps <<std::endl; // Hello, World !
std::cout<< *pd<<std::endl; // 3.14
delete[] pool; // 释放内存池

return0;
}

上述代码中,以下代码:

char* pool = newchar[1024]; // 分配内存池

分配了一个由1024个元素的字符数组,作为内存池使用。

上述代码中,以下代码:

int* pn = new (pool) int (123);

在内存池中分配4个字节作为一个整型变量使用。

上述代码中,以下代码:

char* ps = new (pool + 4) char[15];

在内存池中分配15个字节作为一个字符型数组使用。值得注意的是pool + 4是从内存池的第5个字节开始分配,否则上面分配的整型变量将被覆盖。

上述代码中,以下代码:

delete[] pool; // 释放内存池

在内存池中分配的变量或数组无需释放,只要最后一次性释放整个内存池即可。

5.3 完整代码

本案例的完整代码如下所示:

#include <iostream>

int main(int argc, constchar * argv[])
{

int *p = newint;
    *p = 10;
std::cout<< *p <<std::endl;
delete p;

    p = newint(100);
std::cout<< *p <<std::endl;
delete p;

    p = newint;
free(p);

    p = (int*)malloc(sizeof(int));
delete p;

    p = newint [4] {10, 20, 30, 40};
for (int i = 0; i <4; i++)
std::cout<< p[i] <<' ';
std::cout<<std::endl;
delete[] p;

int (*row) [4] = newint [3][4];
row[1][2] = 15;
delete[] row;

int *p1 = newint;
    *p1 = 200;
delete p1;
//delete p1;

int *p2;
//delete p2;

int *p3 = NULL;
delete p3;

try {
char* p = newchar[0xFFFFFFFF];
p[0] = 'a';
delete p;
   }
catch (std::bad_alloc& ex) {
std::cerr<<"内存分配失败!"<< std::endl;
exit (-1);
    }

char* pool = newchar[1024]; // 分配内存池
int* pn = new (pool) int (123);
char* ps = new (pool + 4) char[15];
strcpy (ps, "Hello, World !");
double* pd = new (pool + 19) double (3.14);
std::cout<< *pn <<std::endl; // 123
std::cout<< ps <<std::endl; // Hello, World !
std::cout<< *pd<<std::endl; // 3.14
delete[] pool; // 释放内存池

return0;
}