第二章—基本语言

第二章—基本语言,第1张

目录

二、命令空间简介、基本输入输出

1、命名空间

2、基本输入输出cin、cout精解 

三、auto、头文件防卫、引用、常量

(1)局部变量及初始化

(2)auto:变量的自动类型推断

(3)头文件防卫式声明

(4)常量(const与constexpr)

const

 constexpr


二、命令空间简介、基本输入输出 1、命名空间

  • (1)简介

同名实体定义:同名函数、同名变量、同名的类定义;
命名空间就是为了防止名字冲突而引入的一种机制。系统中可以定义多个命名空间,每个命名空间都有自己的名字,不可以同名;
可以把命名空间看成一个作用域。在这个命名空间定义函数,和在另一个命名空间定义的函数,即使同名,也互不影响;

  • (2)定义
namespace 命名空间名
{

}

命名空间的定义可以不连续,甚至可以写在多个.cpp文件中,如果以往没有定义这个命名空间,那么 namespace 命名空间名 就相当于定义一个命名空间;如果以往定义了命名空间,那就相当于打开已经存在的命名空间并为其添加新成员的声明。

  • (3)外界如何访问命名空间中的变量、函数

命名空间名::实体名。
其中,::叫作用域运算符 

  • (4)使用命名空间

using namespace 命名空间名
这样以后使用命名空间中的实体就不必再加 (命名空间名::)了 。

2、基本输入输出cin、cout精解 
  •  (1)iostream库:输出输出流

流:就是一个字符序列
cout:console output,是个对象,"标准输出"对象,我们就认为这个对象是屏幕
<< :输出运算符,c++对其进行了重载,
<< :可以当成函数,有参数,例如:cout<<1。参数1在左边,就是cout对象;第二个参数就是 << 右边的实体。可以理解为将 参数2实体 送入参数1(即cout对象,屏幕)
endl:模版函数名,相当于函数指针
endl作用:换行、强制刷新输出缓冲区
输出缓冲区:一段内存,cout输出的时候是往输出缓冲区输出内容,那么输出缓冲区什么时候把内容输出到屏幕去的呢?

  • 1.缓冲区满了
  • 2.程序执行到main的return语句
  • 3.调用了endl,能够强制刷新输出缓冲区(把缓冲区的内容往屏幕上输出)
  • 4.当系统不太繁忙的时候,系统也会查看缓冲区内容,发现新内容也会正常输出到屏幕

<< 定义:ostream& std::cout.operator<<();

<< 返回的是一个写入了给定值(参数2)的cout对象。

  • (2)std::cin

cin:标准输入,"iostream"相关对象,可以就理解成键盘
>> :输入运算符。返回其左侧运算对象作为其计算结果
cin >> val1 >> val2; --> (cin>>val1)>>val2。

三、auto、头文件防卫、引用、常量 (1)局部变量及初始化

C++中,局部变量随时用到随时定义 。

int i={ 5 };     //定义变量的同时初始化变量(c++ 11 ),=可有可无
int a[]{ 1,2,3,4,5 }; //定义数组的同时初始化数组
好处:int a = 3.5f; //可以编译成功
	 int a{ 3.5f }; //无法编译成功,因为3.5f是浮点数,而a是整形
(2)auto:变量的自动类型推断

auto可以在声明变量时,根据变量初始值的类型自动为此变量选择匹配的类型(声明时要赋初值)。 auto自动类型推断发生在编译期间,所以使用auto不会造成程序执行效率降低。

例如:

auto val = true;//bool
auto ch = 'a'; //char
(3)头文件防卫式声明

防止头文件重复包含。

// 法1
#ifndef 标识符  (如果没有用define定义过标识符,则运行程序段)
#define 标识符
	 程序段
#endif

//法2
#pragma once
(4)常量(const与constexpr) const

C语言中的const:

C语言中const修饰的变量是只读变量,在使用const关键字声明定义变量时会给该变量分配内存空间。不能通过变量名直接修改该变量的值,但是可以通过指针的方式修改该变量对应的值,从某种意义上来说,C中const修饰的变量不是真正意义上的常量,可以将其当作一种只读变量。

  • const修饰全局变量时,该变量存储在文字常量区(只读),不能通过指针的方式修改其内容。const修饰的全局变量默认是外部链接的,即其它源文件可以直接使用该变量(但是需要声明一下)。

  • const修饰局部变量,该变量存储在栈区中(可读可写),通过指针的方式可以修改其内容

//fun.c
const int global_a=100; //global_a的本质是只读的变量,存放在文字常量区(内存空间只读)


// test.c

// 对fun.c中的global_a进行声明(不要赋值)
extern const int global_a;
int main()
{
    const int a=10;
    int* point_a=&a;

    a=20;              //error
    global_a=200;      //error
    *point_a = 20;     //ok
    
    *point_a=&global_a;
    *point_a=200;    //error

    
    return 0;
}

C++中的const:

C++中 const修饰的变量不需要创建内存空间,当碰见常量声明时,C++不会单独的给
它分配内存中,而是将他分配在符号表中,符号表的存在形式是一个名字对,比如定义常量const int data = 10;,C++会在一张符号表中添加 namedatavalue为10的一条记录,如下图所示: 

 

  1. 既然,const修饰的变量没有内存空间,所以在C++中const修饰的变量才是真正意义上的常量。
  2. 编译过程中若发现使用常量则直接以符号表中的值替换(符号表不会分配内存) 。
  3. C++中定义声明的全局常量是内部链接的,只能作用于当前的整个文件中,如果想让其它源文件对该常量进行访问的,必须加extern关键字将该常量转换成外部链接。
  4. 编译过程中若发现对 const 使用了 extern(外部变量)或者 &取地址 *** 作符,则给对应的常量分配存储空间(兼容 C),这块空间用常量的值进行初始化。例如有常量a,当对a变量取地址的时候,C++编译器会为a单独的开辟一块内存空间,int* p = (int *)&a;然后这个内存空间赋给指针p,就是p指向这个内存空间。后续修改p指向的空间的值的时候,和常量a没有任何关系。

C 语言中 const 变量是只读变量,有自己的存储空间;

C++中的const常量,可能分配存储空间,也可能不分配存储空间;

  • const常量取地址时会分配空间
  • 使用变量的形式初始化const修饰的变量时会分配空间
  • const修饰自定义数据类型(结构体、对象)时会分配空间
int a = 200;
const int b = a;  // 系统直接为b开辟空间,不会把b放入到符号表中
int* p = (int*)&b;
*p = 3000;
cout << "*p = " << *p << endl;  // 3000
cout << "b = " << b << endl;  // 3000

// 4. const修饰自定义类型的变量,系统会分配空间
const Person per = { 100, "viktor" };

const除了修饰常量外,还可以有 变量只读 的意思。例如:

void func(const int num)
{
    const int count = 24;
    int array[num];            // error,num是一个只读变量,不是常量
    int array1[count];         // ok,count是一个常量

    int a1 = 520;
    int a2 = 250;
    const int& b = a1;    //b 是一个常量的引用,所以 b 引用的变量是不能被修改的
    b = a2;                         // error
    a1 = 1314; //但 const 对于变量 a1 是没有任何约束的,a1 的值变了 b 的值也就变了
    cout << "b: " << b << endl;     // 输出结果为1314
}
  1. 参数 num 表示这个变量是只读的,但不是常量
  2. count 是一个常量,因此可以使用这个常量来定义一个静态数组
  3. 变量只读并不等价于常量
  4. 引用 b 是只读的,但是并不能保证它的值是不可改变的,也就是说它不是常量

const与#define:

  1. const是在编译期间确定好的;#define是在预处理阶段处理好的。
  2. const 常量是由 编译器处理的,提供类型检查和 作用域检查,const有类型,可进行编译器类型安全检查;#define无类型,不能进行类型检查,宏定义由预处理器处理,单纯的文本替换。
  3. 宏的作用域是当前的整个文件,const的作用域以定义的情况决定
void fun1()
{
    #define a 10
    const int b = 20;
    // #undef a 。卸载宏 a
    // # undef .卸载所有宏,将宏定义作用域限制在 fun1 里(卸载宏)
}
void fun2()
{
    printf("a = %d\n", a);    //可以使用,a只是单纯的饿文本替换而已
    //printf("b = %d\n", b);  //不能使用,超出了b的作用域
}
 constexpr
  1. C++11 中添加了一个新的关键字 constexpr,这个关键字是用来修饰常量表达式的。
  2. 所谓常量表达式,指的就是由多个(≥1)常量(值不会改变)组成并且在编译过程中就得到计算结果的表达式。 
  3. 常量表达式和非常量表达式的计算时机不同,非常量表达式只能在程序运行阶段计算出结果,但是常量表达式的计算往往发生在程序的编译阶段,这可以极大提高程序的执行效率;因为表达式只需要在编译阶段计算一次,节省了每次程序运行时都需要计算一次的时间。
  4. C++11 中添加了 constexpr 关键字之后就可以在程序中使用它来修饰常量表达式,用来提高程序的执行效率。在使用中建议将 const 和 constexpr 的功能区分开,即凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr
  5. 在定义常量时,const 和 constexpr 是等价的,都可以在程序的编译阶段计算出结果。

对于 C++ 内置类型的数据,可以直接用 constexpr 修饰,但如果是自定义的数据类型(用 struct 或者 class 实现),直接用 constexpr 修饰是不行的。

例如:

// 此处的constexpr修饰是无效的
constexpr struct Test
{
    int id;
    int num;
};


//要定义一个类常量对象,可以这样写
struct Test
{
    int id;
    int num;
};

int main()
{
    constexpr Test t{ 1, 2 };
    constexpr int id = t.id;
    constexpr int num = t.num;
   
    // t 是一个常量,因此它的成员也是常量
    t.num += 100;  // error,不能修改常量
    cout << "id: " << id << ", num: " << num << endl;

    return 0;
}

为了提高 C++ 程序的执行效率,我们可以将程序中值不需要发生变化的变量定义为常量。

  • 1、使用 constexpr 修饰函数的返回值,这种函数被称作常量表达式函数。constexpr 修饰的函数的返回值,必须要满足以下几个条件:
    • 函数必须要有返回值,并且 return 返回的表达式必须是常量表达式。
      constexpr int func1()
      {
          constexpr int a = 100;
          return a;
      }
    • 整个函数的函数体中,不能出现非常量表达式之外的语句(using 指令、typedef 语句以及 static_assert 断言、return 语句除外)。

      // error
      constexpr int func1()
      {
          constexpr int a = 100;
          constexpr int b = 10;
          for (int i = 0; i < b; ++i)
          {
              cout << "i: " << i << endl;
          }
          return a + b;
      }
      
      // ok
      constexpr int func2()
      {
          using mytype = int;
          constexpr mytype a = 100;
          constexpr mytype b = 10;
          constexpr mytype c = a * b;
          return c - (a + b);
      }

 2、constexpr 修饰模板函数

如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略,即该函数就等同于一个普通函数。

#include 
using namespace std;

struct Person 
{
    const char* name;
    int age;
};

// 定义函数模板
template
constexpr T dispaly(T t) 
{
    return t;
}

int main()
{
    struct Person p { "luffy", 19 };
    //普通函数
    struct Person ret = dispaly(p); //p是变量,实例化后的函数不是常量表达式函数,此时 constexpr 是无效的
    cout << "luffy's name: " << ret.name << ", age: " << ret.age << endl;

    //常量表达式函数
    constexpr int ret1 = dispaly(250); //250是常量,符合常量表达式函数的要求,此时 constexpr 是有效的
    cout << ret1 << endl;

    constexpr struct Person p1 { "luffy", 19 }; //p1是常量
    constexpr struct Person p2 = dispaly(p1);//参数是常量,符合常量表达式函数的要求,此时 constexpr 是有效的
    cout << "luffy's name: " << p2.name << ", age: " << p2.age << endl;
    return 0;
}

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/793137.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存