C++入门

1.命名空间

1.1为什么???

使用命名空间的目的是对标识符进行本地化,避免命名冲突或名字污染。

1.2怎么做???

命名空间的定义:

namespace lhh  //lhh是命名空间的名字

{

}

*命名空间是可以嵌套

*同一个工程中允许存在多个相同名字的命名空间,编译器最后会自动合成一个。ps:一个工程中的test.h和test.cpp中的相同名字的命名空间会合并成一个。

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

命名空间的使用:

namespace lhh
{
 // 命名空间中可以定义变量/函数/类型
 int a = 0;
 int b = 1;
 int Add(int left, int right)
 {
 return left + right;
 }
 struct Node
 {
 struct Node* next;
 int val;
 };
}


int main()
{
 // 编译报错:error C2065: “a”: 未声明的标识符
 printf("%d\n", a);
 return 0;
}

//因此a不能在命名空间外直接使用

法一:命名空间名+作用域限定符

int main()
{
   printf("%d\n",lhh::a);
   return 0;
}

法二:使用using将命名空间中某个成员引入

using lhh::b
int main()
{
 printf("%d\n", lhh::a);
 printf("%d\n", b);
 return 0; 
}

法三:使用using namespace 命名空间名称 引入

using namespce lhh;
int main()
{
 printf("%d\n", lhh::a);
 printf("%d\n", b);
 Add(10, 20);
 return 0; 
}

std是c++标准库的命名空间,c++将标准库的定义实现都放在这个命名空间中。

#include<iostream> ---cout;cin.

c++的输入输出更加方便,不需要手动控制变量类型.

3.缺省参数

是函数在声明或定义时为函数的参数指定的一个缺省值。在调用函数时,如果没有指定的实参则采用该形参的缺省值,否则使用指定的实参。

有全缺省参数和半缺省参数;

注意:

*半缺省参数必须从又往左缺省,不能间隔着给。

*缺省参数不能在函数声明和定义中同时出现。

4.函数重载

是什么???

c++允许在同一作用域中声明几个功能类似的同名函数,他们的形参列表(参数的个数 或 类型 或 类型顺序)不同。常用来处理实现功能类似数据类型不同的问题常用来处理实现功能类似数据类型不同的问题。

4.1 c++支持函数重载的原理

在c/c++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。

通常一个项目又好多个头文件和源文件组成。

如果说,出现以下情况:【当前a.cpp中调用了b.cpp中定义的add函数时,编译后链接前,a.o的目标文件中没有add的函数地址,因为add是在b.cpp中定义的,所以add的地址在b.o中,应该怎么办???】

答案是:在链接阶段,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符

号表中找 Add 的地址,然后链接到一起 。  现在的问题是,如何去找到add函数(使用哪个名字去找?)

*每个编译器都有自己的函数名修饰规则。Windows下的vs修饰规则过于复杂,而Linux下的g++的修饰规则简单。两者原理相同,下面我们用Linux系统做演示:

结论:在Linux下,采用gcc编译完成之后,函数名字的修饰没有改变。

采用g++编译完成之后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

因此:这里就理解了c语言没办法支持重载,因为同名函数没办法区分C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。

补充:在进行多人混合开发的模式下,,由于c和c++编译器ui函数名字修饰规则不同,可能会导致链接失败,这种情况下,我们可以在函数前加extern"C",意思是告诉编译器按照c语言规则来编译。

5.引用

5.1定义:

引用就是给已经存在的变量取别名,编译器不会为引用变量开辟内存空间,它和他的引用变量公用一块内存空间。

int a = 10;
int& ra = a;
int& rra = a;
const int a =10;
const int& b = a;
//错误写法
int& ra;

注意:1、引用和实体是同类型的。

2、引用在定义时必须要初始化。

3、一个变量可以有多个别名。

4、一旦引用了一个实体,就不能再引用其他实体。(即一个别名只能给一个实体)

5.2使用:  作参数、作返回值

首先,我们先看一段代码,代码的输出结果是什么??

int& Add(int a, int b)
{
 int c = a + b;
 return c;
}
int main()
{
 int& ret = Add(1, 2);
 Add(3, 4);
 cout << "Add(1, 2) is :"<< ret <<endl;
 return 0;
}
结果是随机值。
add函数运行结束之后,该函数对应的栈空间就被回收了,即c变量就没有意义了,在main中ret引用add函数返回值,实际引用的就是一块已经被释放的空间。
 注意:1、函数运行时,系统需要给该函数开辟独立的栈空间,用来保存该函数的形参、局部变量以及一些寄存器信息等。                                                                                           2、函数运行结束后,该函数对应的栈空间就被系统回收了。                                                   3、空间被回收指的是该栈空间暂时不能使用,但内存还在。 
如果函数返回时,出了安徽念书作用域,如果返回对象还在(还没有还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。                       

传值和传引用的效率区别:传值,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此传值的效率特别低。

引用和指针的区别:在语法上,引用就是一个别名,没有独立空间,和其引用实体公用同一块空间。                         在底层上,实际是有空间的,因为引用时按照指针方式来实现的。

6.内联函数

6.1为什么???

函数在调用时是有时间和空间的开销的,程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及很多的寄存器都压入栈中,然后才能执行函数体中的代码;等到函数体执行完毕,还要清理现场,将之前压入栈的数据都出栈,才能继续执行函数调用之后的代码。如果函数体代码比较多,需要很长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数短小,频繁的调用在调用上的开销就是不可忽视的。

6.1概念:

inline 修饰 的函数叫做内联函数, 编译时 C++ 编译器会在 调用内联函数的地方展开 ,没有函数调用建立栈帧 的开销,内联函数提升程序运行的效率。   在函数调用出直接嵌入函数体的函数称作内联函数,或者内置函数。
6.2特性:是一种以时间换空间的做法。如果编译器将函数当作内联函数处理,在编译阶段会用函数替换函数调用。
注意:inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
内联函数的缺点也是特别明显:
编译后的程序中会存在多份相同的函数拷贝,如果声明为内联函数的函数体非常大,那么编译之后的程序体积也将变得特别大,所以,要将短小的、频繁调用的函数声明为内联函数。
***** 对函数作inline声明只是程序员对编译器的建议,而不是强制性的,并非指定了inline编译器就必须要这样做,编译器有自己的判断能力。
补充:
宏的优缺点?
优点:
1. 增强代码的复用性。
2. 提高性能。
缺点:
1. 不方便调试宏。(因为预编译阶段进行了替换)
2. 导致代码可读性差,可维护性差,容易误用。
c++有哪些技术代替了宏?
1、常量定义换用const enum
2、短小函数定义 换用内联函数

使用宏代码的最大缺点就是容易出错,预处理在拷贝宏代码时常常产生意想不到的边际效应。

内联机制既具备了宏代码的效率又增加了安全性,而且可以自由操作类的数据成员。因此,应该尽量使用内联去代替宏代码

注意:要在函数定义的时候添加inline关键字,在函数声明时添加虽然没有错,但是编译器会自动忽略。

7.auto关键字

7.1为什么??

1、类型难以拼写 2、含义不明确导致容易出错

7.2是什么??

作为一个新的类型指示符来只是编译器,auto声明的变量必须有编译器在编译时推到而得

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推到auto的实际类型。因此,suto并非是一种类型的声明,而是一个类型声明时的占位符,编译器的编译期会将auto替换为变量实际的类型。

注意:1、用auto声明指针类型时,用auto和auto*没有任何区别,但是用auto声明引用类型的时候必须要加&

int x = 10;
auto a = &x;
auto* b = &x;  //声明指针类型的时候加* 与不加都可以
auto& c = x;    //引用类型必须加

2、auto不能作为函数的参数类型,编译器无法进行推导

3、auto不能用来直接声明数组

8.基于范围的for循环

1、范围for的语法

void TestFor()
{
   int array[] = { 1, 2, 3, 4, 5 };
   for(auto& e : array)
    e *= 2;
 
     for(auto e : array)
     cout << e << " ";
     return 0;

     //for循环后的括号由 :分为两部分,第一部分时范围内用于迭代的变量,第二部分时表示迭代的范围
}

2、使用条件: ***for循环跌代的范围必须是确定的:对于数组而言时第一个元素到第二个元素的范围,对类而言是提供begin与end的方法   ***迭代的对象要实现++和==的操作。

9.指针空值nullptr

字面常量 0 既可以是一个整形数字,也可以是无类型的指针 (void*) 常量,但是编译器默认情况下
将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转 (void *)0
注意:
1. 在使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr C++11 作为新关键字引入的
2. C++11 中, sizeof(nullptr) sizeof((void*)0) 所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr
错题补充:
1、double compare(int,int)的重载函数
a.int compare(double,double)  是上面函数的重载函数 是参数的类型不同
b.int compare(int,int)                不是上面函数的重载函数,重载函数不能依靠函数的返回值的不同构成函数重载
2、引用传值,指针传地址 ---错误
因为:引用表面好像是传值,其本质也是传地址,只是这个工作有编译器来做。
3、指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。指针是间接操作对象,引用时对象的别名,对别名的操作就是对真实对象的直接操作。
4、使用inline关键字的函数会被编译器在调用处展开---错误
因为:不一定,因为inline只是一种建议,需要看此函数是否能够成为内联函数。
5、头文件中可以包含inline函数的声明----错误
因为:inline函数不支持声明和定义分离开,因为编译器一旦将一个函数作为内联函数处理,就会在调用位置展开,即该函数是没有地址的,也不能在其他源文件中调用,故一般都是直接在源文件中定义内联函数的。
6、可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数----正确
因为:.inline函数会在调用的地方展开,所以符号表中不会有inline函数的符号名,不存在链接冲突。

    

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值