C++基础:第三章 对象与基本类型

文章目录

    • 第3章 对象与基本类型
      • 第1节 初始化/赋值语句
      • 第2节 类型详述
        • 类型描述
        • 类型种类划分
        • 与类型相关的标准未定义部分
        • 变量
      • 第3节 复合类型:从指针到引用
          • 指针
            • 指针特点
            • 相关操作符
            • 指针的定义
            • `void*`指针
            • 指针的指针
            • 指针v.s.对象
            • 指针的问题:
          • 引用
            • 指针的引用
      • 第4节 常量类型与常量表达式
        • 常量
        • 常量指针与顶层常量(top-level const)
        • 常量引用
        • 常量表达式
      • 第5节 类型别名与类型的自动推导
          • 类型别名
          • 类型的自动推导
      • 第6节域与对象的声明周期

第3章 对象与基本类型

第1节 初始化/赋值语句

初始化的基本操作:

  • 在内存中开辟空间(堆或栈),保存相应的数值
  • 在编译器中构造符号表,将标识符与相关内存空间关联

值与对象都有类型

第2节 类型详述

类型是编译期概念,可执行文件中没有数据类型的概念

C++是强类型语言

引入类型是为了更好的描述程序,防止误用

类型描述
  • 存储需要的尺寸(sizeof ,标准并没有严格限制)(一种类型对应的是汇编语言中一条简单的易处理的语句,汇编语句与程序的硬件相关)
  • 取值空间(std::numeric_limits ,超过范围可能溢出,上溢出变为最小值,下溢出变成最大值)

std::cout<::min()<

  • 对齐信息 (alignof(int)查看对齐信息)CPU读取内存中数据过程经过cache缓冲区,一次读取的大小为cache的大小,读取的地址为cache的整数倍,以int为例,如果在内存中存储的地址为7999 - 8002,那么在读取8000 - 8063后数据没有读取完整,会再次读取8000-64 - 7999段的数据,所以系统存储数据时会按照数据类型的对其信息存储,如8000 - 8003(数据类型的对其信息的整数倍),由于结构体内变量的对齐信息,结构体所占长度不一定等于所有变量长度加和。
struct Str{
    int x;
    char y;
};

​ 例如Str的长度为8而不为5

  • 可执行的操作
类型种类划分
  • 基本(内建)类型:C++语言中支持的类型
    • 数值类型
      • 字符类型:char,wchar_t,char16_t,char32_t
      • 整数类型
        • 带符号整数 short,int,long,long long
        • 无符号整数 unsigned + 带符号整数 (unsigned == unsigned int)
      • 浮点类型 float,double,long double
    • void
  • 复杂类型:由基本类型组合、变种所产生的类型,可能是标准库引入或类型定义。
与类型相关的标准未定义部分
  • char是否有符号,取决于编译器

​ 可以通过定义unsigned char/signed char定义有符号和无符号

  • 整数在内存中的保存方式:大端、小端(两台机器之间传输数据时要考虑,解析时要一致)

  • 每种类型的大小

    • C++11中引入固定尺寸整数类型,如int32_t

字面值:在程序中直接表示为一个具体的数值或字符串的值

每个字面值有其类型

  • 整数:20(十进制),024(八进制),0x14(十六进制) int

  • 浮点数: 1.3 1e-8 double

  • 字符: ‘c’ ‘\n’ char

  • 字符串:“hello” char[6]型(字符串后隐式加上\0表示字符串结束)

  • 布尔:true false boor

  • 指针:nullptr nullptr_t

float x = 1.3;					//double转为float 
unsigned long long y = 2ULL;    //int转为unsigned long long 

(可以引入自定义后缀改变字面值类型 User-defined literals)

int operator "" _ddd(long double x)
{
	return (int)x*2;
}

int x = 3.14_ddd;
变量

对应一段存储空间,可以改变其中内容

变量类型在首次声明(定义)时指出:

  • int x;定义
  • extern int x;不同编译端的声明 (如果在声明时赋值,系统会认为是定义)

变量的初始化和赋值

  • 初始化:构造变量之初赋予值
    • ​ 缺省初始化(不赋值)如果变量缺省初始化在函数内,则赋予一个随机值,如果时全局变量,缺省初始化赋值为0。因为函数内定义变量时,多数要对变量进行操作,变量的初始化赋值是不必要的,为提升效率默认在初始化时不赋值,内存中为上一次程序在此处内存中存储的值。全局变量的初始化是在整个程序开始时只执行一次,代价较小,所以为保证程序每次执行的一致性,默认为初始变量缺省赋值为0;
    • 直接/拷贝初始化 直接:int x(10); 拷贝:int x = 10;

变量赋值时可能设计类型的转换(高精度转为低精度时可能有损

  • bool与整数之间转换 bool x = 1;(为0则为false,非0为true),bool转为整数对应0和1
  • 浮点与整数之间

隐式类型转换

  • if 判断
  • 数值比较
    • 无符号数与带符号数进行比较时,会把带符号数转化为无符号数
    • 可以使用std::cmp_XXX(C++ 20) 比较,满足数学定义。

第3节 复合类型:从指针到引用

指针

一种间接类型
C++基础:第三章 对象与基本类型_第1张图片

指针表示存储变量的首个字节的地址

指针特点
  • 指向不同对象
  • 具有相同的尺寸(各种类型的指针,在64位机上通常大小都为8个字节)

(64位机 总线长度是64个字节,一次性处理64个字节,内存最大为264

相关操作符
  • 取地址&
  • 解引用*
int* p = &x;    //*表示声明变量p是一个指针
int  y = *p;	//*表示解引用,访问指针所指向的变量
指针的定义
int main(){
	int* p = &x; 
	int* p(&x); 
	int* p;         //函数内缺省初始化,存放随机值(原内存中的数据被解析成一个地址)
}
int* p;				//函数外缺省初始化,初始化为0,0地址特殊,不能被解引用  

int* p = nullptr;

C语言中 NULL==0函数内尽可能避免地址缺省初始化,可赋值为0

nullptr

  • 一个特殊类型的对象,类型为nullptr_t,表示空指针
  • 类似于C中的NULL,但更安全

函数重载

fun(int){}     //1
fun(int*){}    //2

//函数调用时会自动选择输入参数类型匹配的函数
int* p;
fun(0);        //调用1
fun(p);		   //调用2
fun(nullptr)   //调用2

指针与bool类型的隐式转换,if(p)语句中如果p指向0(空指针)地址则转换为false,否则为true,等同于if(p!=nullptr)

指针可以增加和减少,指针+1表示增加一个对应类型所占长度

int x = 42;
int* p = &x;
p=p+1;

指针还可判等和比较,当两个指针分别指向不同变量时不建议比较两个指针,因为所分配的地址不固定,与在堆或栈中建立有关。

void*指针
  • 没有记录对象的尺寸信息,可以保存任意地址(因为指针的尺寸是相同的,等同于硬件总线长度,所以可以声明一个占位的指针,并与其他类型的指针转化)(由于没有类型信息,故不可以加减)
  • 支持判等操作
指针的指针

C++基础:第三章 对象与基本类型_第2张图片

指针v.s.对象

指针为间接调用,可以避免直接调用产生的数据传输。复制成本较低,读写成本较高。

struct Str
{
	//.....这是一个较大的结构体
};
void fun(Str param)
{

}

int main(){
	Str x;
	fun(x);  //这种情况会产生较大的数据传输
}
struct Str
{
	//.....这是一个较大的结构体
};
void fun(Str* param)
{

}

int main(){
	Str x;
	Str* p = &x;
	fun(p);  //这种情况可以避免大数据传输,因为指针的大小为固定的8个字节(硬件总线决定)
}
指针的问题:
  • 可以为空
  • 地址信息可能非法
  • 解决方案:引用
引用
  • int& ref = val;
  • 是对象的别名,不能绑定字面值
  • 在构造时绑定对象,在其生命周期内不能绑定其他对象(赋值操作会改变对象内容)
  • 不存在空引用,但可能存在非法引用(初始化为一个临时变量如函数内的变量)——总的来说比指针安全(作为函数的入口参数
void fun(int& param){
	//.......
}
  • 属于编译期概念,在底层还是通过指针实现
指针的引用
  • 指针也是对象,可以定义引用

第4节 常量类型与常量表达式

常量

与变量相对,表示不可修改的对象

  • 使用const声明常量

  • 是编译期概念,编译器利用常量

    • 防止非法操作
    • 优化程序逻辑
    int x = 3;
    if(x = 4){      //如果此处错写,程序执行完全不同,为避免要const x = 3;
    	//....
    }
    
    const int x = 3;
    //......
    int y = x + 1;    //对比int x = 3;编译器默认x值不变,直接赋值y=4,减少一次读x操作
    
常量指针与顶层常量(top-level const)
  • int* const ptr = &x; 常量指针,表示指针不能被修改
  • const int* ptr = &x; 表示不能通过ptr修改所指向地址处的值

区分:如果const出现在*右边,则表示指针不能被修改,如果const出现在*左边,则表示指针指向的内容不能被修改

解引用:

int* ptr1;
*ptr1;      		//int类型
int* const ptr2;
*ptr2;     			//int类型
const int* ptr3;
*ptr3;				//const int类型
常量引用
  • const int& ref = x;
int x = 3;				
const int& ref = x;   //加强限制,只能读不能写
const int x = 3;				
int& ref = x;   	  //放松限制,报错 
  • 主要用于函数的形参
struct Str{
    //.....
};
void fun(const Str& param){
    //可以避免大量拷贝(引用),同时防止数据被写改变(常量引用)
}
int main(){
    Str x;
    fun(x);
}
  • 常量引用可以绑定字面值(为保证常量引用做函数形参时,使用字面值调用函数)
常量表达式

C++11开始

  • 使用constexpr来声明 (不是一种类型,是一种类型的限定符)
  • 声明是编译期概念
  • 编译器可以利用其进行优化
  • 常量表达式指针

第5节 类型别名与类型的自动推导

类型别名

可以为类型引入别名,从而引入特殊的含义或便于使用(如size_t)同一种类型在不同的硬件中的长度可能不同,为了避免同一程序在不同硬件上执行的一致性

两种引入类型别名的方式

  • typedef int MyInt;
  • using MyInt = int;(C++11开始)

使用using方式更好

  • typedef char MyCharArr[4];
  • using MyCharArr = char[4];

类型别名与指针、引用的关系

  • 应将指针的类型别名视为整体,在此基础上引入常量指针表示指针为常量的类型
  • 不能构造引用的引用
类型的自动推导
  • auto(C++11开始)从初始化表达式自动推导变量的类型

  • 并不意味着弱化对象类型(python弱对象类型语言)

  • 几种常见形式

    • auto x = 6.5; 会产生类型退化

    类型退化:当变量放在等号右侧时,变量的类型会发生改变。典型的类型退化有引用、常量等(int& ref = x; int y = ref;此处的ref的类型会退化成int类型。const int x = 3; int y = x;

    • auto&可以避免类型的退化
    • decltype(exp); 返回表达式的类型(类似于auto,但不会产生退化)(对一般意义表达式左值加引用
    int x = 3;
    int* ptr = &x;
    
    decltype(x);         //变量       不加引用  返回int  
    decltype(x);         //表达式 左值 加引用    返回int& 
    decltype(*ptr);		 //表达式 左值 加引用	 返回int&
    decltype(3.5+5l);	 //表达式 右值 不加引用	返回double
    
    • decltype(val); 返回val的类型

    • decltype(auto) x = 3.5 + 15l;等同于下面的

      decltype(3.5 + 15l) x = 3.5 + 15l;C++14开始

    • concept auto

第6节域与对象的声明周期

域(scope)表示程序中的一部分,其中名称有唯一的含义

全局域(global scope):程序最外围的域,其中定义的是全局变量

块域(block scope): 使用大括号所限定的域,其中定义的是局部对象

还存在其他的域:类域,名字空间域

域可以嵌套,嵌套域中定义的名称可以隐藏外部域中定义的名称

对象的生命周期起始于被初始化的时刻,终止于被销毁的时刻

  • 全局对象的生命周期是整个程序的运行期间
  • 局部对象的生命周期起源于被初始化位置,终止于所在域被执行完成

你可能感兴趣的:(C++,c++)