C++入门基础知识

目录

1.关键字(c++98)

2.命名空间

3.缺省值

4.输入输出

5.函数重载

6.引用

7.内联函数

8.auto

9.nullptr(c++11)


1.关键字(c++98)

c++(98)的关键字比c语言多了几乎一倍

2.命名空间

c语言无法解决命名冲突的问题,如与库中的函数名称冲突,项目中各成员的区块代码之间冲突

因此引进命名空间的概念,即namespace

int a = 5;
void f1()
{
    int a = 0;
    printf("%d\n",a);//输出0,默认在局部找
    printf("%d\n",::a);//::称为域作用限定符,前面若为空,则默认在全局寻找
}

void f2()
{
    int a = 1;
    printf("%d\n",a);//输出1
}

int main()
{
    printf("%d\n",a);//输出5,现在局部main函数中找,没找到,再到全局找找到a=5
    f1();
    f2();
    return 0;
}
//但是值得注意的是,在f1中无法寻找f2中的a

命名空间 ----命名空间域,只影响使用,不影响生命周期

namespace anode
{
    struct node
    {
        int val;
        struct node* next;
        struct node* pre;
    };

    int x = 0;
}//不需要加引号

namespace bnode
{
    struct node
    {
        int val;
        struct node* next;
        struct node* prev;
    };

    int x = 0;
}
int main()
{
    struct anode::node n1;
    struct bnode::node n2;

    anode:: x++;
    bnode:: x++;
    return 0;
}

同时1.在多个文件中同名的命名空间会合并

        2.命名空间可以嵌套

namespace node
{
    namespace  A
    {
        struct node
        {
            int val;
            struct node* next;
            struct node* pre;
        };
    }
    
    namespace   B
    {
        struct node
        {
            int val;
            struct node* next;
            struct node* pre;
        };
    }
}

int main()
{
    struct node::A::node n1;
    return 0;
}

而using namespace std ;即为全局展开c++标准库

也可以部分展开,比如 using std::cout;

3.缺省值

(1)全缺省

void  print(int a = 1, int b = 2, int c = 3)
{
    printf("%d\n",a);
    printf("%d\n",b);
    printf("%d\n",c);
}
//支持以下几种方式
int main()
{
    print(4,5,6);
    print(4,5);
    print(4);
    print();

    return 0;
}

只能从右往左依次缺省,若传值则使用传入的值,否则使用缺省值

(2)半缺省

void  print(int , int b, int c = 3)
{
    printf("%d\n",a);
    printf("%d\n",b);
    printf("%d\n",c);
}
void  print(int a , int b = 2, int c = 3)
{
    printf("%d\n",a);
    printf("%d\n",b);
    printf("%d\n",c);
}

缺省值也是只能从右往左依次缺省

ps 如果声明和定义分离,那么缺省值只能在声明中给

4.输入输出

c++使用 

>>流提取

<< 流插入
 endl等效于 '\n'
 

int main()
{
    cout << "hello world" << endl;

    int a;
    cin >> a;
    cout << a << endl;

    int * arr = (int *) malloc(sizeof(int)*5);
    for(int i = 0; i < 5; i++)
    {
        cin >> arr[i];
    }

    for(int i = 0; i < 5; i++)
    {
        cout << arr[i] << endl;
    }

    return 0;;
}

cin 与 cout 会自动识别类型

5.函数重载

函数参数的类型,数量,顺序不同均可构成函数重载

void sum (int x, int y)
{
    ;
}

void sum (int x, int y, int z)
{
    ;
}

void sum(int x, char y)
{
    ;
}

void sum(char x, int y)
{
    ;
}

并且与返回值无关,返回值的不同不构成函数重载

在各个编译器上,编译器会对函数名称进行修饰,不同编译器修饰规则会不同

以下是一种简单的示例

比如 _zsumii      _zsumic   (i 为int 的首字母, c为char的首字母)

编译器会识别修饰之后的函数名

6.引用

简单来说就是给变量取别名,

1.一个变量可以有多个别名,变量的别名也可以再取别名

2.引用必须在初始化的时候就赋值

int  i = 0;
int& j = i;
int& k = i;

int& l = j;

3.引用一旦引用一个实体,就不能再引用其他实体
例如:

int a = 10;
int b = 20;
int& ref = a; 
ref = b;//这里的赋值是错误的

可以用于规避二级指针带来的复杂性,使代码更简洁

struct list
{
    int data;
    srtuct list* next;
};


void buylist(list*& node, int x)
{
    
    list* tmp = (list*)malloc(sizeof(list));
    if(tmp == nullptr)
    {
       perror("failed :");
       return;
    }
    if(node == nullptr)
    {
        node = tmp;
    }
}

知识补充

int Count1()
{
    int a = 0;
    a++;

    return a;
}

int Count2()
{
    static int b = 0;
    b++;

    return b;
}

int main()
{
    int ret1 = Count1();
    int ret2 = Count2();

    return 0;
}

        这两个函数属于传值返回。无论是第一个还是第二个函数,即不管变量出作用域之后是否销毁,都会产生一个临时变量来充当返回值。如果比较小就存在寄存器中,大的话会提前在上一层函数中开好空间0。

而如果使用传引用返回,返回的是其别名,那么就不会产生拷贝的过程,并且可以修改返回对象

int& Count3()
{
    static int c = 0;
    c++;

    return c;
}

int main()
{
    int ret3 = Count3();
    Count3() = 5;
    return 0;
}

使用传引用返回的条件是出了作用域,这个变量还在,就可以使用传引用返回

临时变量具有常性,不能修改(可以理解为被const修饰)

另外,指针进行初始化和赋值以及引用进行初始化的时候,权限可以缩小,但是不能放大,因为对于指针来说,两个变量里的指针指向的是同一块空间,而对于引用来说,一个变量改变可以直接影响另一个

下面是错误代码示例

const int a = 2;
int & b = a;

const int *c = & a;
int* & d = c;

这里是正确代码示例

const int a = 2;
const int & b = a;

const int *c = & a;
const int* & d = c;
int a = 2;
const int & b = a;

int *c = & a;
const int* & d = c;

但是其它类型,比如说int,就没有这样的限制,因为它只是进行一个拷贝

此外,类型转换也会产生临时变量

int i = 0;
double & j = i; //这个编译不能通过
const double & k = i; //这个可以编译通过

i类型转换产生一个临时变量,k是这个临时变量的别名

引用比指针要更安全一些。

并且引用的底层是指针,虽然在语法上引用不会再去开空间,但是实际上是会的。

7.内联函数

使用宏函数不建立栈帧

c++推荐用const ,enum替代宏常量

inline替代宏函数

#define  ADD(x, y)((a) + (b))//写一个ADD的宏函数

宏的缺点:

1.不能调试

2.没有类型安全的检查

3有些场景下非常复杂,容易出错

内联函数也不建立栈帧,但是在默认的debug版本不起作用,因为那样就不能调试了,release下会起作用,编译的时候会在调用内联函数的地方展开

注意点

1.内联函数是以“空间”换时间的做法,这个空间指的是指令。

比如说一个add函数的指令是十行,调用一千次,指令就是1000+10行,每次调用都是先call,再jmp跳转

而如果它是内联函数,那么指令大约是10*1000行(粗略),因为每次调用都会把函数展开

2.内联函数主要用于程序短,调用频繁的函数

3.内联函数不建议声明和定义分离,因为内联展开了,就没有函数地址了,链接就会找不到

最后inline只是一种建议,使用inline关键字之后不一定该函数就一定会成为内联函数,编译器会自行进行判断

8.auto

可以自动识别类型,简化代码

int a = 1;
auto b = a;

map <string,string> dict;
auto dit = dict.begin();

可以用于范围for遍历数组

int arr[] = {1,2,3,4};
for(auto a : arr)
{
    cout << e << " ";
}
cout << endl;

但是这个语法是把数组中的数据赋值给a,a只是数组中值的拷贝。

如果想要改变数组的值可以使用引用

for (auto& a : arr)
{
    a = a * 2;
}

但是这个仅仅可以用于数组,如下代码是编不过的,下面函数中传的实际上是地址

void print(int arr【】)
{
    for(auto a : arr)
    cout << a << " ";
}

auto在同一行声明多个变量时,这些变量必须要类型相同,否则编译器会报错,因为编译器实际上只对第一个类型进行推导,然后用推导出来的类型定义其它变量。

auto a = 1, b = 2;

auto 不能作为函数的参数

void add(auto a)
{
    ;
}

auto 也不能用来声明数组

auto arr【】 = {1,2,3};

9.nullptr(c++11)

可以理解为使用void *强转的0值。因为在c++中NULL的值就是一个int类型的0.

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值