PoEdu - C++阶段班- Lesson02_C to C++

 

 


 

1  原生bool类型

 

  c++里面的bool类型才是真正原生的true和faul,比如常见的大写的“BOOL”,它就不是原生的。

原生的与非原生的bool,它们的区别:

详细说下原生bool与非原生bool之间的差别:

_Bool 这个是c98之后加上的bool类型,和我们原生bool等同。

C++中,建议一直使用原生bool,它是最安全的类型。

补充一点:宏定义一个int类型的bool,之所以这么做,是因为0是false,1是true,在C++中,这种比对是有风险的,可以说是不正确的。

 

 

 


 

2 头文件的三种标准

    在vs2015上默认建立一个项目,VS2015帮我们生成4个文件:

stdafx.h这个是微软给我们的一个大坑。

 

有可能会导致一个问题:预编译头会导致平台不兼容。

文件后缀cpp和c,编译器会选择不同的编译方式编译。

在以DomainOperatorDemo这个解决方案命名的工程中,生成的DomainOperatorDemo.cpp,有程序员直接改名为main.cpp。告诉别人main方法在这里。

在stdafx.h中,默认包含了3个头文件。我们可以直接在main函数中使用printf()。

其中还包含了tchar.h,这是微软给我们的一个坑,它是windows特定的头文件,它前面有个t,带有很强的平台特性。不要过于依赖这个头文件。

  

以c++中,stdio.h和cstdio功能一样。

 

 

 

3 补充预编译头:提问stdafx.cpp具体的作用是什么呢?

    可以理解为微软的VC特有的文件,实际上我们可以完全的没有它,直接创建空项目。是给编译器用的,并不是我们所需要的。预编译头,想是给我们一定的便利,但并没有什么卵用。

 


 

4  域运算符  

它的作用主要就是来解决一件事:

 

 

 看示例: 

变量不是最终的本质,本质是内存里的表述数据。

 

 

 作用域访问符::

变量的覆盖原则:就近访问。谁最近,我就去找它。

命名空间与作用域:

  在C语言中,我们了解到,变量的生命周期和作用域,就是看这个变量放在哪,外部的、全局的、局部的,局部的局部的等,在C中对变量的作用域控制力是很弱的。

  所以在C++中,特意加入了一个权限概念:命名空间,加强了作用域的控制。除了全局空间,栈空间,又多了一个自已定义的命名空间。

 当域作用符前面为空的时候,表示为全局空间,域作用符前面有名称的,就是命名空间

std是个什么样子的命名空间?看图:

如何自定义一个命名空间:

自定义命名空间除了解决变量重定义的问题,它还有其它的作用吗?答案是有的:工业编程里面,大的项目是很多程序员合作的结果,当另一个伙伴想要访问你一个同名的变量时,命名空间就能很好的分隔区分它们。

 


 

5  new&delete    运算符

 

  前面讲到了全局,讲到了栈,现在说说“堆”:

c中我们学到2个操作函数是 malloc()  和  free()。C++中间添加了new 和 delete ;

#include <iostream>

int main()
{
    int *pNum = new int(100);
    std::cout << *pNum;

    return 0;
}

运行:100

在C语言中maloc()一定和free()对应使用,那么C++中也是:new一定对应delete使用。

#include <iostream>

int main()
{
    int *pNum = new int(100);
    std::cout << *pNum;
    delete pNum;
    return 0;
}

运行:100

 再看:如果new一个数组呢,怎么释放?

#include <iostream>

int main()
{
    int *pNum = new int(100);
    std::cout << *pNum;
    
    int *pArray = new int[10];
    
    delete pNum;
    delete[] pArray;
return 0;
}

运行:100

注意:new  和  delete  是运算符,它们不是函数。重要的说三遍!老师就在课堂里强调了三次。

 


 6 什么条件下能够构成重载

看代码,我们在C中是这么写函数的:

#include <cstdio>

void MyCoutInt(int num)
{
    printf("%d", num);
}

void MyCoutChar(char c)
{
    printf("%c",c);
}

void MyCoutFloat(float f)
{
    printf("%f",f);
}

void MyCoutString(char *str)
{
    printf("%s", str);
}

int main()
{

    return 0;
}

以上在C中,各个函数因为参数不同,而函数名也要不同,不然就重定义了。C++中,可以这样子写:

#include <cstdio>

void MyCout(int num)
{
    printf("%d", num);
}

void MyCout(char c)
{
    printf("%c",c);
}

void MyCout(float f)
{
    printf("%f",f);
}

void MyCout(char *str)
{
    printf("%s", str);
}

int main()
{

    return 0;
}

以C++方式编译:通过~!没有问题!    以前C中是完全不能够的,因为函数名重复了。

改成C方式编译:

#include <stdio.h>

void MyCout(int num)
{
    printf("%d", num);
}

void MyCout(char c)
{
    printf("%c",c);
}

void MyCout(float f)
{
    printf("%f",f);
}

void MyCout(char *str)
{
    printf("%s", str);
}

int main()
{

    return 0;
}

编译:

1>------ 已启动生成: 项目: OverloadeDemo, 配置: Debug Win32 ------
1> main.c
1>e:\c_code\overloadedemo\overloadedemo\main.c(9): error C2084: 函数“void MyCout(int)”已有主体
1> e:\c_code\overloadedemo\overloadedemo\main.c(3): note: 参见“MyCout”的前一个定义
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

 

改回C++模式,编译就通过,这个例子引入C++的一个机制:重载  

重载——允许函数名相同,但后面的参数一定是不同的。如果函数名相同,参数也相同,就不能构成重载:

#include <cstdio>

void MyCout(int num)
{
    printf("%d", num);
}

void MyCout(int num)
{
    printf("%d", num);
}

void MyCout(char c)
{
    printf("%c",c);
}

void MyCout(float f)
{
    printf("%f",f);
}

void MyCout(char *str)
{
    printf("%s", str);
}

int main()
{

    return 0;
}

编译:

1>------ 已启动生成: 项目: OverloadeDemo, 配置: Debug Win32 ------
1> main.cpp
1>e:\c_code\overloadedemo\overloadedemo\main.cpp(9): error C2084: 函数“void MyCout(int)”已有主体
1> e:\c_code\overloadedemo\overloadedemo\main.cpp(3): note: 参见“MyCout”的前一个定义
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

 

讲了半天,重载会有什么好处?

 

#include <cstdio>

void MyCout(int num)
{
    printf("%d", num);
}

void MyCout(char c)
{
    printf("%c",c);
}

void MyCout(float f)
{
    printf("%f",f);
}

void MyCout(char *str)
{
    printf("%s", str);
}

int main()
{
    int num = 100;
    char c = 'a';
    char *str = "I Love Mark";
    float f = 1.0002;
    MyCout(f);
    MyCout(str);
    MyCout(num);

    return 0;
}

下断点,运行:

1.000200I Love Mark100

看下C++中cout里面运算符的重载:

 再看重载的具体原理,它具体的原理是什么;它里面有些什么潜规则呢?
下个断点,看看重载函数调用的顺序:

 

 

 看跳转到哪:

 

其实本质上,C++最终还是生成了4个函数,表面上函数名一样,其本内里是不一样的4个函数。在调用这个函数的时候,编译器会帮我们选择一一对应的函数。根据函数的参数去匹配。

用反汇编查看调用匹配:

充分说明了,内里是不同的,不是表面上看到的一样的。

为什么编译器能给我们一一对应匹配呢?

因为编译器根据符号表里函数名,参数类型,域空间,等元素的不同来重定向函数名:

我们可以假设内里重载的函数名会改名:函数名@类型@类@命名空间……比如:?GetArryLens@MyString@@QAEIXZ

c++里面增加针对函数重载的“命名重定向”与“命名粉碎”的说法:你实际存在符号表里的函数名,被加了参数类型等附加名。这里就有一个问题:

名称被重定向了,命名粉碎了,内里名称已经被更改了,这时当别人调用这个函数的时候,名称已经不是这个我们命名名称了,这里如果别人想要你真实的函数名,会很困扰,这时应该怎么办?这个时候可以加一个前缀extern c :可以使用extern"c"(c编译模式)阻止命名粉碎。

在C中,没有命名粉碎这一概念的。

上面重载都讲参数类型的不同,同名函数重载,那如果这个函数的返回值不同,会不会影响重载失败呢?

重载与否跟返回值没关系,只与函数名和参数有关系:

看测试代码 :

#include <cstdio>

void MyCout(int num)
{
    printf("%d\n", num);
}

void MyCout(char c)
{
    printf("%c\n",c);
}

void MyCout(float f)
{
    printf("%f\n",f);
}
//MyCout@char*        //假设按命名重定向,命名为MyCout@char*
void MyCout(char *str)
{
    printf("%s\n", str);
}
//MyCout@char*   //这里也是一样,生成和上面同名了。     
int MyCout(char *str)
{
    printf("%s\n", str);
    return 0;
}

int main()
{
    int num = 100;
    char c = 'a';
    char *str = "I Love Mark";
    float f = 1.0002;
    MyCout(f);
    MyCout(str);
    MyCout(num);

    return 0;
}

编译:报错!

1>------ 已启动生成: 项目: OverloadeDemo, 配置: Debug Win32 ------
1> main.cpp
1>e:\c_code\overloadedemo\overloadedemo\main.cpp(24): error C2556: “int MyCout(char *)”: 重载函数与“void MyCout(char *)”只是在返回类型上不同
1> e:\c_code\overloadedemo\overloadedemo\main.cpp(18): note: 参见“MyCout”的声明
1>e:\c_code\overloadedemo\overloadedemo\main.cpp(23): error C2371: “MyCout”: 重定义;不同的基类型
1> e:\c_code\overloadedemo\overloadedemo\main.cpp(18): note: 参见“MyCout”的声明
1>e:\c_code\overloadedemo\overloadedemo\main.cpp(34): warning C4305: “初始化”: 从“double”到“float”截断
1>e:\c_code\overloadedemo\overloadedemo\main.cpp(36): error C2264: “MyCout”: 函数定义或声明中有错误;未调用函数
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

 

命名重定向时,不考虑返回值,关键是参数的类型,参数的多少。重定向没有重名就能重载。

#include <cstdio>

void MyCout(int num)
{
    printf("%d\n", num);
}

void MyCout(char c)
{
    printf("%c\n",c);
}

void MyCout(float f)
{
    printf("%f\n",f);
}
//MyCout@char*
void MyCout(char *str)
{
    printf("%s\n", str);
}
//MyCout@char*@int
int MyCout(char *str,int num)
{
    printf("%s\n", str);
    return 0;
}

int main()
{
    int num = 100;
    char c = 'a';
    char *str = "I Love Mark";
    float f = 1.0002;
    MyCout(f);
    MyCout(str);
    MyCout(num);

    return 0;
}

编译通过。

 只要最终命名粉碎后,没有重命名,则能构成重载。

posted on 2016-12-20 18:06  zzdoit  阅读(343)  评论(0编辑  收藏  举报

导航