C++各种概念比较

内存动态分配和静态分配在这里插入图片描述

时间不同
	静态分配发生在程序编译和链接的时候
	动态分配发生在程序调用和执行的时候
空间不同 
	堆
		动态分配的
			由程序员释放内存
	栈
		静态分配
			编译器完成,如局部变量
		动态分配
			函数malloc分配
				栈的动态分配有编译器进行释放

类似 动态绑定和静态绑定

动态语言、静态语言、脚本语言、解释型语言、编译型语言

静态 变量类型在编译时确定
动态 变量类型在运行时确定
添加链接描述

数组 数组名的内涵比指针丰富得多

使用数组作为参数传递时,数组名将退化为指针,指针指向数组首元素的地址

在这里插入图片描述

函数指针与指针函数

函数指针
	指向函数的指针,使函数可以像参数一样被传递
	函数名就是一个指针,可以赋值给函数指针
int (*fun)(int a,int b); 
int sum(int a,int b){
    
     return a+b;}  
fun = sum; 
int s=(*fun)(3,5); 
指针函数
	返回类型为指针的函数
		int * sum(int a,int b)

引用和指针 简单说几句区别

引用是C++加入的语法,本质是一个const pointer

**做函数参数时**
//1. 值传递
void mySwap01(int a, int b) {
    
    
int temp = a;
a = b;
b = temp;
}
//2. 地址传递
void mySwap02(int* a, int* b) {
    
    
int temp = *a;
*a = *b;
*b = temp;
}
//3. 引用传递  
void mySwap03(int& a, int& b) {
    
    
int temp = a;
a = b;
b = temp;
}

int main() {
    
    
int a = 10;
int b = 20;
mySwap01(a, b);
cout << "a:" << a << " b:" << b << endl;
mySwap02(&a, &b);
cout << "a:" << a << " b:" << b << endl;
mySwap03(a, b);//似乎直观一点
cout << "a:" << a << " b:" << b << endl;
system("pause");
return 0;
}

指针是一个独立的变量,有自己的内存空间。。。而引用只是个别名,不能单独存在
可以有空指针,但没有空引用,引用必须初始化
使用sizeof,指针大小是4或8字节,而引用则是被引用对象的大小
使用++运算符意义不一样
指针被初始化后可以指向其他对象,而指针一旦指向某个对象就不能更改
指针需要解引用&,引用可以直接操作数据
指针可以有多级指针,而引用只有一级,不存在引用的引用!!!*

在这里插入图片描述

在这里插入图片描述

堆heap和栈stack的区别

管理方式
堆中的资源由程序员控制new/delete,容易造成内存泄漏
栈资源由操作系统编译器自动管理,如局部变量入栈出栈,不需要程序员写代码分配内存

在这里插入图片描述

string、string.h和cstring 头文件区别

string和cstring是c++标准库的东西,位于std名字空间。

string.h是c语言的库,用于处理char *类型的字符串。
string是c++标准库STL中的一个类,它实际上是basic_string模版类实例化产生的。
cstring兼容了过去string.h的函数,但是采用了c++的写法。

最后CString和cstring还有区别前者是mfc中的一个类。

strlen和sizeof区别?

sizeof是C语言的一种单目操作符,如C语言的其他操作符++、–等。它并不是函数
sizeof 可以求 类型 或者 对象 在内存中占用的字节数**(存储大小)**
sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。

strlen是字符处理的库函数。 库 string.h和cstring

sizeof参数可以是任何数据的类型或者数据(sizeof参数不退化);strlen的参数只能是字符指针且结尾是’\0’的字符串。
因为sizeof值在编译时确定,所以不能用来得到动态分配(运行时分配)存储空间的大小。

  int main(int argc, char const *argv[]){
    
    
      
      const char* str = "name";

      sizeof(str); // 取的是指针str的长度,是8
      strlen(str); // 取的是这个字符串的长度,不包含结尾的 \0。大小是4
      return 0;
  }

new/delete和malloc/free的区别

**new/delete是C++加入的操作运算符,调用的是赋值运算符重载,它们是C++关键字,需要C++编译器支持不需要头文件支持
malloc/free是C中的库函数,需要头文件stdlib.h函数支持**

看实际写法分析区别

// malloc则必须由我们计算字节数,并且在返回的时候强转成实际指定类型的指针。
int *a  = (int *)malloc ( sizeof (int ));
free(a); 

//new返回指定类型的指针,并且可以自动计算所需要的大小。
int *a  = new int;
delete a;

//free 不管你的指针指向多大的空间,均可以正确地进行释放,这一点释放比 delete/delete [] 要方便。
int *p1 = (int *)malloc(sizeof(int) * length);
free p1;

int *p2 = new int[length];
delete[] p2; 

//补充一个new使用
char* p=new char[6];    
    //p="Hello";                 
  //不能将字符串直接赋值给该字符指针p,由于指针p指向的是字符串的第一个字符,只能用下面的                                
strcpy(p,"Hello");    
cout<< *p <<endl;   //只是输出p指向的字符串的第一个字符!    
cout<< p <<endl;   //输出p指向的字符串!    
delete[] p;  

在这里插入图片描述
补充几句
5.重载
C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。
6. 内存区域
new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。

extern 与 global(global不是关键字。)

extern可以用局部变量吗? 不能

extern只能声明 没有被static修饰,也就是没有被把作用域缩小限制在本文件的 全局变量

extern最基本的用法是声明在其他C文件定义的全局变量的。这里需要注意两点,一是“声明”,二是“全局变量”;我们先来分析这两个概念。
**声明:声明和定义是有区别的。**声明不等于定义,
声明只是指出了变量的名字,并没有为其分配存储空间;类比类中声明友元函数。

定义指出变量名字同时为变量分配存储空间,定义包含了声明。

extern变量称为外部存储变量. 用于声明在另一个C源文件中定义的变量。 可以说,extern声明了程序中将要用到但尚未在本文件定义的外部变量。
一个工程是由多个c文件组成的。这些源代码文件会分别进行编译,然后链接成一个可执行的模块。把这样的一个程序作为一个工程进行管理,并且生成一个工程文件来记录所有包含源代码文件。

## global不是关键字。
一个变量是不是 global 变量是由作用域决定的,当变量在全局作用域中声明时,这个变量就是 全局 变量

memcpy与strcpy

1)复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

2)复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

3)用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

char *strncpy(char *dest, const char *src, size_t n)

把source中的n个字符或空字符之前的字符(先满足哪个条件就拷贝到何处)拷贝到target中。因此,如果source中的字符数小于n,则拷贝整个字符串,包括空字符。但是,strncpy()拷贝字符串的长度不会超过n,如果拷贝到第n个字符时还未拷贝完整个源字符串,就不会拷贝空字符。所以,拷贝的副本中不一定有空字符。鉴于此,该程序把n设置为比目标数组大小少1(TARGSIZE-1),然后把数组最后一个元素设置为空字符。


char * strcpy(char * dest, const char * src) // 实现src到dest的复制
strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符。

C 库函数
char *strncpy(char *dest, const char *src, size_t n)
把 src 所指向的字符串复制到 dest,最多复制 n 个字符。
当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
dest – 指向用于存储复制内容的目标数组。
src – 要复制的字符串。
n – 要从源中复制的字符数。


void *memcpy(void *dest, const void *src, size_t n);

功能
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

所需头文件
C语言:#include<string.h>
C++:#include<cstring>

返回值
函数返回指向dest的指针。

C++强制类型转换

C++ 类型转换(C风格的强制转换):

在C++基本的数据类型中,可以分为四类:整型,浮点型,字符型,布尔型。其中数值型包括 整型与浮点型;字符型即为char。

(1)将浮点型数据赋值给整型变量时,舍弃其小数部分。

(2)将整型数据赋值给浮点型变量时,数值不变,但是以指数形式存储。

(3)将double型数据赋值给float型变量时,注意数值范围溢出。

(4)字符型数据可以赋值给整型变量,此时存入的是字符的ASCII码。

(5)将一个int,short或long型数据赋值给一个char型变量,只将低8位原封不动的送到char型变量中。
(6)将有符号型数据赋值给长度相同的无符号型变量,连同原来的符号位一起传送。

static_cast、const_cast、reinterpret_cast和dynamic_cast

C++语言中新增了四个关键字**static_cast、const_cast、reinterpret_cast和dynamic_cast。这四个关键字都是用于强制类型转换的。

1) static_cast
static_cast用于数据类型的强制转换,强制将一种数据类型转换为另一种数据类型。


 1.1 自动类型转换(隐式)

```cpp
关键字	         			说明
static_cast			用于良性转换,一般不会导致意外发生,风险很低。
const_cast			用于 const 与非 const、volatile 与非 volatile 之间的转换。
reinterpret_cast	高度危险的转换,这种转换仅仅是对二进制位的重新解释,不会借助已有的转换规则对数据进行调整,但是可以实现最灵活的 C++ 类型转换。
dynamic_cast		借助 RTTI,用于类型安全的向下转型(Downcasting)。

语法格式为: xxx_cast<newType>(data)

static_cast

静态 ===联想到 在编译期间
static_cast 是“静态转换”的意思,也就是在编译期间转换,转换失败的话会抛出一个编译错误。

常用于简单数据类型的转换,例如将整型数据转换为浮点型数据。

int a = 10;
int b = 3;
//double result = (double)a / (double)b;
double result = static_cast<double>(a) / static_cast<double>(b);dynamic_cast类型转换符的作用下,是不可以**将基类指针转换到派生类指针**的,**所以使用static_cast能够实现类似于这种的向下转换**。比如可以将double转换为int**当然向上转型也是可以的,对于类的强制转换仅限类与类之间是要有继承关系的,无关的类则无效。**


由于对于数值类型的向下转换是可能出现精度损失的,因此使用static_cast强制转换就是告诉编译器我们是在知情并接受这样的精度损失的前提下进行的。

## const_cast
const_cast 运算符仅用于进行**去除 顶层const 属性**的转换,它也是四个强制类型转换运算符中唯一能够去除 const 属性的运算符。

**const_cast是可以改变指向一个值的指针,但更改了常量特征的指针所指向的那个值若是const的,仍然是不可以修改的。
只能改变去掉顶层const,也就是说去掉指针自身的const属性**

```cpp
int a = 10;
int* const b1 = &a;        //顶层const,b1本身是一个常量
const int* b2 = &a;       //底层const,b2本身可变,所指的对象是常量
const int b3 = 20; 		   //顶层const,b3是常量不可变
const int* const b4 = &a;  //前一个const为底层,后一个为顶层,b4不可变
const int& b5 = a;		   //用于声明引用变量,都是底层const

执行对象拷贝时有限制,常量的底层const不能赋值给非常量的底层const
使用命名的强制类型转换函数const_cast时,只能改变运算对象的底层const

对于const_cast,它只能在同类型之间转型,所以它更加安全,避免在改变某个对象的常量特征时不小心修改了它的类型。

将 const 引用转换为同类型的非 const 引用,将 const 指针转换为同类型的非 const 指针时可以使用 const_cast 运算符。

const string s = "Inception";
string& p = const_cast <string&> (s);
string* ps = const_cast <string*> (&s);  // &s 的类型是 const string*

dynamic_cast

该运算符的作用是在有继承关系的这种类层次结构中进行向上转换,不允许其他转换。
将派生类的指针类型 转换为基类的指针类型

class A {
    
    
...
};
class B : public A{
    
    
...
};

int main() {
    
    
	A* a;
	B* b;
	a = dynamic_cast<A*> (b);
	//b = dynamic_cast<B*> (a);  ERROR,只能向上转型
}

reinterpret_cast

reinterpret_cast是可以进行无关的类型之间的转换的,它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的比特位。但这种转换的任意性必然存在很多问题,那么在什么情况下使用它是最重要的。

非常危险!!!不应该随便使用

它的一个应用就是用来辅助哈希函数(代码转自MSDN的例子)

// expre_reinterpret_cast_Operator.cpp
// compile with: /EHsc
#include <iostream>
// Returns a hash code based on an address
unsigned short Hash( void *p ) {
    
    
	unsigned int val = reinterpret_cast<unsigned int>( p );
	return ( unsigned short )( val ^ (val >> 16));
}

using namespace std;
int main() {
    
    
	int a[20];
	for ( int i = 0; i < 20; i++ )
		cout << Hash( a + i ) << endl;
}

//如果-是64位的系统,可能需要将unsigned int改成 unsigned long才能运行。

猜你喜欢

转载自blog.csdn.net/qq_46084757/article/details/127005370
今日推荐