命名空间_C&CPP区别_auto_const_new和delete


定义简单类型变量

int a = 5; // C & CPP
int a(5); // CPP
char * str = “12345”; // c & cpp
char * str(“china”); // cpp
// CPP宽字符
wchar * str(L”呵呵”);
wcout << str; // 宽字符的输出 wcout,而不是cout

宽字符和窄字符的区别:

  • char属于C和C++的最常见的基本数据类型,长度为一个字节,只能存储ASII码。
  • 但是下面这行代码不会报错但是c的值却不是汉字‘汉’,因为只有一个字节,所以无法存储,打印出来是乱码。char c = ‘汉’;

建议采用CPP方式,括号初始化。

命名空间namespace

  1. namespace作用:解决类重名、函数重名、变量重名等重名问题。

  2. namespace内容的访问类型:

    • namespace中所有的内容都是public的,且不能用private修饰。
    • 但是cpp中struct可用public、private修饰,且结构体默认数据是public的。
    • class中默认内容是私有private的
  3. C没有namespace关键字,cpp才有命名空间概念,这也是在cpp中头文件无后缀.h的原因(区分是c的还是cpp的头文件(cpp版本头文件中可用namesapce))

    iostream是C++的头文件,iostream.h是C的头文件,即标准的C++头文件没有.h扩展名,将以前的C的头文件转化为C++的头文件后,有时加上c的前缀表示来自于c,例如cmath就是由math.h变来的。 iostream.h里面定义的所有类以及对象都是在全局空间里,所以你可以直接用cout    但在iostream里面,它所定义的东西都在名字空间std里面,所以你必须加上    using namespace std才能使用cout
  4. 命名空间的定义:namespace SPACE_NAME { body…}

  5. 命名空间的引用

    • a) using namespace SPACE_NAME;
    • b) SPACE_NAME::成员NAME
  6. 匿名namespace:匿名namespace,表示可直接引用该space内的内容,而不用SPACE_NAME::的方式调用该域内容

  7. 命名空间别名

        namespace name1_long_name{}
        namespace name2 = name1_long_name;//变量赋值的方式为命名空间取别名
  8. 命名空间嵌套:命名空间可以嵌套

  9. 扩充命名空间内容

    • 再次重定义命名空间(空间名相同)则会扩展原命名空间的内容,而不像变量那样因重定义而编译出错。
    • 但是,在扩展的时候,不能再次重定义变量,不会覆盖而是发生重定义编译错误。
    • 不建议在命名空间中直接定义函数,而是采用函数指针变量的方式,在命名空间外部定义函数
  10. using作用域

    • using作用只从该语句到本源文件结束。
    • 若using语句在block内部,则该using只作用在该代码块中
    • using语句只能在命名空间定义之后的地方使用
    • 一个源文件可用同时使用多个using语句,但是若同时using的多个命名空间有名称相同的内容,则引用该内容时需要显式的采用域操作符::方式。
  11. C中全局变量与CPP命名空间

    • C中,当函数中局部变量与全局变量重名时,在该函数中无法引用该全局变量
    • CPP中,可用::操作符解决上述情景,可在函数中引用重名的全局变量,::name表示name是全局的变量,而不是该代码块中的局部变量
    • 综上所述,cpp中的::域操作符,若之前有标识符,表示命名空间,无标识符,表示全局变量

函数重载

  • 重载:参数类型,参数个数,参数顺序,函数名相同
  • C没有函数重载,但是cpp有函数重载,故cpp兼容c时,需要用到extern关键字。
  • 函数的默认参数:C不可以,cpp可以使用默认参数,但是默认参数只能在参数列表的最后。
  • 含有默认参数的重载函数,在调用的时候会发生调用错误,编译器不知道到底该调用哪个函数,此时,可用函数指针解决该问题,或者用namespace解决。

    void test(int a,int b=1)
    {
    cout<< a << b;
    }
    
    void test(int a)
    {
    cout << a;
    }
    
    int main(int argc, char const *argv[])
    {
    // 调用test的时候,若test(2);则不知道如何匹配
    // 此时用namespace或者函数指针解决
    void (*fun1)(int a,int b) = test;
    // 用指针解决的时候,默认参数不再起作用,要显式传入参数值
    fun1(2,1);  
    return 0;
    }

auto

  • C与cpp中auto都是自动变量(自动匹配类型),但有所不同
  • C中无法获取类型。
  • cpp中,可以使用auto循环遍历数组,语法类似于java的foreach,必须是一个数组的常量指针

附:cout打印的时候会默认屏蔽小数点后无意义的0,如1.000打印成1.
CPP类型转换,C方法括号,CPP方法static_cast

左值与右值,引用(难点)

  • 最大的区别:是否可取地址,是否可赋值。左值必须在内存中有实体,而通常右值暂存在cpu的寄存器中
  • 左值引用:int a = 5;int &ra(a);
  • 指针的引用:int * pa(&a);int * &rpa = pa;//rpa是pa的副本,别名,引用,类型是int*的
  • 引用右值:如,取地址是右值,CPU寄存器中,右值引用常用于操作底层cpu。例见代码
  • 函数返回引用:返回的是栈上局部临时变量的引用值。区分引用的是栈中内容还是堆中内容。
 // 左右值引用的语法和区别
 // int 变量,初始为5
    int a(5);
    // int类型的指针,指向a
    int * pa(&a);
    // int指针 类型的左值引用,引用的是指针变量pa
    int * & rpa1(pa);
    // int指针 类型的右值引用,引用的是int类型数据a的地址
    //(取地址在CPU中完成,结果保存在cpu的寄存器中,即&a这个值是在寄存器,&a这个值对应的地址是在内存中的)
    int* && rpa2 = &a;
/* 堆栈的引用,试验结果与笔记有所出入,vs下qt下结果略有不同 */
// 返回值为:栈中内容的引用
int & test1()
{
    // a 是栈中局部变量,函数调用结束回收内存
    int a = 10;
    int &ra = a;
    return ra;
}

// 返回值为:堆中内容的引用
int * & test2()
{
    // 通过new申请的堆内存,不会随函数调用结束而被回收重利用
    int * a = new int(100);
    cout << "a指向的堆地址:" << a <<endl;
    cout << "指针变量a自身的地址(栈变量地址):" << &a <<endl;
    int * &ra = a;
    return ra; // 相当于返回的是指针变量a(的 别名\副本 变量)
}

int main(int argc, char *argv[])
{
    // int类型引用接收函数返回值
    int & ra = test1();
    // 输出结果a=10;
    /* 原因:ra是左值引用,实际是通过指针实现,与被引用变量操作同一块内存
     * 而现在test1中的a是栈中局部变量,函数调用后,不再占用该内存,也就是说
     * ra指向的是一块它不再持续占用的内存,但此时,这个栈内存还没有被其他操作
     * 占用,故其中的内容没变,故访问其内容,输出的还是10 */
    cout << "a = " << ra <<endl;
    // 输出结果a=574548;
    /* 原因:cout实际是操作符重载函数的调用,在函数调用过程中,占用了栈内存。
     * 故tset1中的a的内存,也就是ra的实际内存,被操作了,修改成了任意函数调用
     * 中间值,故此时再访问,输出就不是10了,而是任意值 */
    cout << "a = " << ra << endl;
    cout << endl;

    // -----------------------------------------------
    // 这是一个int指针的副本,这个int指针变量是栈中局部变量,随时会被清除重利用
    int * & pra = test2();
    // 故先将该指针所指向的堆内存地址保存下来
    int * p = pra;

    cout << *pra << endl;
    cout << *p << endl;

    cout << *pra << endl;
    cout << *p << endl;

    delete(p);

    cout << *pra << endl;
    cout << *p << endl;


    return 0;
}

引用是CPP内容,C不支持,引用必须初始化, = 或者 () 两种初始化方法
引用变量和被引用变量操作的是同一块内存,但指针和引用不完全相同。
malloc和free,new 和delete区别,C中free的指针还能访问,但CPP中为了安全,delete之后的指针,实际上是指向了一块安全的内存区域,该区域禁止访问,访问就会程序中断。

const

  • CPP中可用const int常量值来初始化数组长度,而c不能通过编译。const int num=4;int a[num];
  • CPP中,一旦const,则无法修改。C中可以通过指针修改。但CPP中可通过const_cast方式强制去除const属性(但仍然无法修改),此处坑点:不同编译器会做不同处理,实际上常量内存已通过指针修改,但是有的编译器在读取常量的时候直接从常量表中读取替换,而不从内存读取,感觉就是指针没有修改const数据,比如vc的编译器就是这样的
  • 几种const写法的差别:常指针与指向常量的变指针
int a = 10;
const int b = 5;

// 此处 const int == int const
// 在*左边,表示p1指向的是int常量,指向的内存中数据不能修改
// 但p1本身是个指针变量,p1可修改,即可指向其他地址
const int * p1 = &a;
int const * p2 = &b;

p1 = &b; // 合法,p1可改变指向
p2 = &a; // 合法,同上

*p1 = 3; // 不合法,p1指向的内存是const int,常量,不能修改
*p2 = 6; // 不合法,同上

int * const p3(&a); // 合法,p3是常指针,不能修改指向,但指向的内存可修改存储数据
int * const p4(&b); // 不合法,p3指向的内存规定是int型,但&b是const int 型,不匹配
p3 = &b; // 不合法,p3是常指针,无法修改指向
p4 = &a; // 不存在
*p3 = 4; // 合法,p3指向的内存可修改,但需要改成int型
*p4 = 8; // 不存在

const int * const p5(&a);// 指向常量的常量指针
//注:此处a是变量而不是常量,但操作仍成功,因为此处p5只读,不能修改a的值,a的类型是否const无意义,编译器无视
  • 常引用:拷贝构造函数参数就是常引用,保证不会修改被引用的数据

通常常量标识符,用大写

new delete

  • int num; int *p = new int;前者栈中,后者堆中
  • VC中,delete p,再访问*p,会无法访问产生中断。CPP中无法重复delete一个指针两次
  • delete数组,基本数据类型,可直接delete,复杂数据类型,需要 delete []p
  • 重载new和delete时,若需要调用默认的new和delete,使用全局域操作符::new和::delete
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值