c++基础(1)

2024-07-17 08:20:27 浏览数 (1)

c语言是结构化和模块化的语言,用于处理规模较小的程序。当问题需要高度抽象和建模时,c语言不适合。c 是基于c语言产生的,既可以进行c语言过程化程序设计,又可以以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计

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

如果像下面这种情况,在c语言中是解决不了的:

代码语言:javascript复制
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
  printf("%dn", rand);
  return 0;
}

编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”

命名空间定义

命名方法:使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{} 中即为命名空间的成员。

我们有不同的命名空间定义方法:

1、正常的命名空间定义

命名空间中可以定义变量/函数/类型

代码语言:javascript复制
namespace pearl
{
   int rand=10;
   int Add(int left, int right)
   {
      return left   right;
    }
   Struct Node
   {
      Struct Node* next;
      int val;
    };
}

2、命名空间可以嵌套

//test.cpp

代码语言:javascript复制
namespace pearl1
{
   namespace pearl2
   {
      int Add(int a,int b)
      {
         return a b;
      }
    }
}

3、同一个工程中可以出现命名相同的空间,并且编译器最终会合成在同一个命名空间当中

//test.h

代码语言:javascript复制
namespace pearl1
{
   int Sub(int a,int b)
   {
      return a-b;
    }
}

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

命名空间的使用

比如我们在命名空间当中定义了一个变量,我们该如何把它调用出来呢?

代码语言:javascript复制
namespace pearl
{
   int a=1;
   int b=0;
   int rand=10;
   int Add(int left, int right)
   {
      return left   right;
    }
   Struct Node
   {
      Struct Node* next;
      int val;
    };
}
int main()
{
   printf("%d",a);
   return 0;
}

// 编译报错:error C2065: “a”: 未声明的标识符

命名空间的使用有三种方法:

1、加空间名称以及域作用限定符

代码语言:javascript复制
int main()
{
    printf("%dn", pearl1::a);
    return 0;
}

2、使用using将某个空间成员引入

代码语言:javascript复制
using pearl::b;
int main()
{
    printf("%dn", b);
    return 0; 
}

3、使用using namespace命名空间名称引入

代码语言:javascript复制
using namespce pearl;
int main()
{
    printf("%dn", pearl::a);
    printf("%dn", b);
    Add(10, 20);
    return 0;    
}

这里a和b都能够被正常打印出来。 

c 输入和输出

代码语言:javascript复制
#include<iostream>
// std是C  标准库的命名空间名,C  将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
   cout<<"Hello world!!!"<<endl;
   return 0;
}

注意:

使用cout(标准输出对象(控制台))和cin(标准输入对象(键盘))时,必须包含<iostream>标准头文件以及按照命名空间使用方法使用std。

cout和cin是全局的流对象,endl是特殊的C 符号,表示换行输出,他们都包含在包含< iostream >头文件中。

是流插入运算符,>>是流提取运算符。并且在c 中的输入和输出可以自动识别变量的类型。

std命名空间的使用惯例

在日常练习中,建议直接using namespace std即可。

如果在大型的开发项目当中,直接展开,会出现比较多的问题,像std::cout这样使用时指定命名空间 using std::cout展开常用的库对象/类型等方式就可以了。

缺省参数

概念

是在C 声明或定义函数时,为函数的参数指定一个默认值。在调用该函数时,如果没有为某个参数指定实参(即没有显式地传递一个值给这个参数),则该函数将自动采用该参数的默认值。如果调用时提供了实参,则使用指定的实参值。

分类

全缺省参数
代码语言:javascript复制
void Func(int a = 10, int b = 20, int c = 30)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }
半缺省参数
代码语言:javascript复制
void Func(int a, int b = 10, int c = 20)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }
注意事项

1半缺省参数必须从右往左给,不能跳着给。

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

如果声明与定义位置同时出现缺省参数,恰巧两个位置提供的值不同,那编译器就无法确定到底用哪个缺省值。

3缺省值必须是常量或者全局变量

函数重载

概念

在同一作用域,使用相同名称且功能类似的同名函数,这些同名函数的形参列表不同,来处理不同数据类型的问题。

1、参数类型不同
代码语言:javascript复制
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{
 cout << "int Add(int left, int right)" << endl;
 return left   right;
}
double Add(int left, int right)
{
 cout << "double Add(int left, int right)" << endl;
 return left   right;
}
2、参数个数不同
代码语言:javascript复制
void Add()
{
   cout << "f()" << endl;
}

void Add(int a)
{
   cout << "f(int a)" << endl;
}
3、参数顺序不同
代码语言:javascript复制
void f(int a, char b)
{
 cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
 cout << "f(char b, int a)" << endl;
}

那么为什么c语言支持函数重载,而c 不支持呢

review:一个程序需要运行起来,需要经过预处理,编译,汇编,链接这四个过程。

实际的项目通常由多个源文件和多个头文件组成,在编译后链接前,当addB.obj中调用的函数地址只在addA.obj(在addA.cpp中定义)中存在,我们就会进行链接。链接器在看到addB.obj调用obj时,就会到addA.obj中的符号表找到Add的地址,然后链接到一起。

每个编译器都有自己的命名规则,那么链接器会在哪儿寻找Add函数呢?

c语言

在Linux下,采用gcc编译后,函数名的修饰不发生改变。

c

在Linux下,采用g 编译后,函数名的修饰发生改变。 会在编译过程中将函数的参数类型信息(以及其他可能需要的信息,如模板参数等)添加到函数的名字中。

引用

定义

引用不是定义一个新的变量,而是原有的变量有了新的别名,并不需要为这个别名开辟一个新的空间,它和引用的变量公用一块存储空间。

规则 :引用类型&引用变量名=引用实体名;

代码语言:javascript复制
int &ra=a;

引用变量必须和引用实体是同种类型的。

引用特性

1引用前必须初始化

2一个实体可以被多次引用

3一个引用变量名只能使用一次,不能再引用其他实体

常引用

下面我将会逐个讲解下面代码错误的地方,以及如何修正

代码语言:javascript复制
void TestConstRef()
{
    const int a = 10;
    int& ra = a;  
    const int& ra = a;
    int& b = 10; 
    const int& b = 10;
    double d = 12.34;
    int& rd = d; 
    const int& rd = d;
}

 const int a = 10;     int& ra = a;     错误 ,不能将非常量引用绑定到常量对象上。

   const int& ra = a;  正确

2  int& b = 10; 

错误,不能将非常量引用绑定到字面量上

 const int& b = 10; 正确

3      double d = 12.34;       int& rd = d;     错误,类型不同

const int&rd=d; 正确

权限可以缩小,但不能被放大。

用法

1作为参数
代码语言:javascript复制
void swap(int&left,int&right)
{
   temp=left;
   left=right;
   right=temp;
}
2作为函数的返回值
代码语言:javascript复制
int &count()
{
  static int n=0;
  n  ;
  return n;
}
代码语言:javascript复制
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变量就没有意义了。但是空间被回收指的是空间不能使用,仍然可以通过引用找到这个值。 

在调用函数结束后,如果返回对象还在(未还给系统),可以使用引用返回,如果返回对象已经还给系统,就要使用传值返回。

传值传址效率比较

采用传值返回时,返回的并不是值本身,而是该实参或者返回变量的一份临时拷贝,因此效率非常低下。

在语法层面上,引用是不占空间的。

但是在底层逻辑层面,引用需要占一块空间。

代码语言:javascript复制
int main()
{  
   int a=10;
   int &ra=a;
   ra=20;
   
   int *p=&a;
   *p=20;
   return 0;
}

引用和指针的区别

1 引用必须要初始化,指针可以不用初始化

2 引用在引用时只能引用一个实体,而多个指针可以指向同一个实体

3sizeof的结果不同,引用中计算的是引用内容的大小,而指针是地址空间的大小

4引用中 1是引用的实体加一,而指针是偏移一个类型大小

5指针需要显示解引用,指针只需要自己处理

6引用比指针更加安全

内联函数

概念

以inline修饰的函数叫作内联函数,在gcc编译器下,只有内联函数才会展开

auto关键字

编译器会通过变量初始化的表达式来推断变量的类型。(编译器在编译时期推导而得)它并不是一种类型的声明,而是一种类型的占位符。编译器在编译时期会替换成实际的类型。

auto的使用细则

0 人点赞