C++学习——布尔型,操作符别名,函数和引用

本文深入探讨C++的布尔类型、函数重载、内联函数、动态内存分配、引用及其实现细节,涵盖操作符别名、哑元参数、缺省参数等高级特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

六、C++的布尔类型(bool)

1.bool类名是C++中的基本数据类型,专门负责表示逻辑值:真/假

注:C语言的C99标准里加入了布尔类型

2.bool类型内存占一个字节,1表示true,0表示false

3.bool类型的变量可以接收任意类型表达式的结果,其值非0则为true,0则为false

bool func()
{
    return true/false;
}

4.插入流控制符`boolalpha`:以字符串的形式打印bool值(就是打印true/false不打印数字)

cout << "b=" << boolalpha << b << endl; 

参考代码:

#include <iostream>
using namespace std;
int main(void)
{
    bool b = false;
    cout << "size=" << sizeof(b) << endl;//1
    cout << "b=" << b << endl;//0
    b = 3+5;
    cout << "b=" << b << endl;//1
    b = 1.2 * 3.4;
    cout << "b=" << b << endl;//1
    char* p = NULL;//NULL-->(void*)0
    b = p;
    cout << "b=" << b << endl;//0
    return 0;
}

七、操作符别名

&& <==> and
|| <==> or
!  <==> not
{  <==> <%
}  <==> %>
......

参考代码:

#include <iostream>
using namespace std;
int main(void)
<%
    int a=3;
    int b=0;
    if(a or b)<%
        cout << "true" << endl;
    %>
    else<%
        cout << "false" << endl;
    %>
    return 0;
%>

 

★八、C++函数

1.函数重载

1)定义:在相同作用域下,定义同名的函数,但是他们的参数必须有所区分,这样的多个函数构成重载关系

//C语言方式实现	
void drawRect(int x,int y,int w,int h){}
void drawCircle(int x,int y,int r){}
......
//C++方式实现
void draw(int x,int y,int w,int h){}
void draw(int x,int y,int r){}
......

函数重载特征:

①相同的作用域(在同一个类中)

②函数名相同

③参数不同(与返回值类型,参数常属性,volatile和默认参数无关),或者其常函数:当编译器决定哪个函数被声明,定义或调用时,每个参数的const和volatile类型和修饰和默认参数会被忽略掉。

特例:

void f(int * x)
void f(const int *x)或void f(int const *x)   // 编译正常
//但是,第二个不能为void f(int * const x)

void f(int * x)
void f(volatile int *x)或void f(int volatile *x)   // 编译正常
//但是,第二个不能为void f(int * volatile x)

④virtual关键字可有可无没有影响

参考代码:

#include <iostream>
using namespace std;
int func(int i){
    cout << "func(int)" << endl;
}
void func(int i,int j){
    cout << "func(int,int)" << endl;
}
void func(int a,float b){
    cout << "func(int,float)" << endl;
}
int main(void)
{
    func(100);
    func(100,200);
    func(100,1.23f);

    //通过函数指针调用重载关系的函数时,由
    //函数指针的类型决定其匹配的重载版本
    void (*pf)(int,float) = func;
    pf(100,200);

    return 0;
}

2)函数重载匹配

调用重载关系函数时,编译器将根据实参和形参的匹配程度,自动选择最优的版本

当前g++编译器的匹配规则:

完全匹配 >= 常量转换 > 升级转换 > 降级转换 > 可变参数(...)

参考代码:

#include <iostream>
using namespace std;
//char->int:升级转换
void foo(int i){
    cout << "foo(1)" << endl;
}
//char->const char:常量转换
void foo(const char c){
    cout << "foo(2)" << endl;
}
//short->char:降级转换
void fun(char c){
    cout << "fun(1)" << endl;
}
//short->int:升级转换
void fun(int i){
    cout << "fun(2)" << endl;
}
//省略号匹配(最差)
void bar(int i,...){
    cout << "bar(1)" << endl;
}
//double->int:降级转换
void bar(int i,int j){
    cout << "bar(2)" << endl;
}
int main(void)
{
    char c = 'A';
    foo(c);
    short s = 10;
    fun(s);
    bar(10,1.23);
}

3)函数重载的原理:
C++编译器通过对函数进行换名,将参数表的类型信息整合到新的名字中,解决函数重载和名字冲突的矛盾

问:extern "C"的作用?

答:在C++函数声明时加入extern "C",可以强制要求C++编译器不对该函数进行换名,方便C程序调用该函数

注:extern "C"声明的函数不能重载

extern "C" void func(void)
{
    ......;
}

2.哑元参数

1)定义:定义函数时,只有类型而没有变量名的形参称为哑元

void func(int)//哑元参数
{
    ......;
}

2)应用场景:
①操作符重载时区分前后++/--

②兼容旧代码

//算法库
void video_math(int a,int b){...;}
//使用者
int main()
{
    video_math(10,20);
    ......;
    video_math(30,40);
    ......;
}
_____________________________________
//算法库省级
void video_math(int a,int)//第二个参数为哑元参数
{
    video_math(10,20); //不影响旧代码的函数调用
    ......;
    video_math(30,40);
    ......;
}

3.函数缺省参数(默认参数)

1)可以为函数的部分参数或全部参数指定缺省值,调用该函数时,如果不给实参,就取缺省值作为相应的值

2)靠右原则,如果一个参数有缺省值,那么该参数右侧的所有形参都必须带有缺省值

3)如果函数的定义和声明分开,缺省参数应该写在函数的声明部分,而定义部分不写

#include <iostream>
using namespace std;

void func(int a,int b=200,int c=300); //函数的声明

int main()
{
    func(111);//111,200,300
    func(111,222); //111,222,300
    func(111,222,333); //111,222,333
}

void func(int a,int b/*=200*/,int c/*=300*/) //函数的定义
{
    cout << "a=" << a << ",b=" << b << ",c=" << c << endl;
}

4.内联函数(inline)

1)定义:使用inline关键字修饰函数即为内联函数,编译器会尝试对内联进行优化,避免函数的调用开销,提高程序的执行效率

2)使用说明:

①多次调用小而简单的函数适合内联

②调用次数极少或者大而复杂的函数不适合内联

③递归函数不能内联

④虚函数不能内联

注:内联函数只是一种建议而不是强制要求,一个函数能否内联优化取决于编译器,有些函数不加inline关键字也会默认处理为内联优化,有些函数即便加了inline修饰也会被编译忽略

 

九、动态内存分配

1.C++使用操作符实现动态内存的分配

分配:new/new[]

释放:delete/delete[]

错误处理:异常机制

#include <iostream>
using namespace std;

int main(void)
{
    //int* pi = new int;
    //*pi = 100;
    //分配内存同时初始化
    int* pi = new int(100);
    cout << *pi << endl;
    delete pi;//防止内存泄露
    pi = NULL;//放置野指针

    int* pi2 = new int(100);
    (*pi2)++;
    cout << *pi2 << endl;//101
    delete pi2;
    pi2=NULL;

    /*int* parr = new int[10];
    for(int i=0;i<10;i++){
        parr[i] = i;
        cout << parr[i] << ' ';
    }*/
    //new数组同时初始化,C++11
    int* parr = 
        new int[10]{1,2,3,4,5,6,7,8,9,10};
    for(int i=0;i<10;i++){
        cout << parr[i] << ' ';
    }
    cout << endl;
    delete[] parr;
    parr=NULL;
    return 0;
}

2.C++中的new/delete和C中malloc/free的区别和联系

1)两者都是在堆上进行的内存操作

2)new与delete只能在C++中使用,但malloc和free可以在C++和C中使用,new与delete是操作符,可以重载,malloc和free是库函数

3)使用new分配内存的时候,会自动相应类的构造函数进行初始化,而malloc只是单纯的分配内存,不进行初始化,delete释放内存时会调用相应类的析构函数,free单纯的释放内存

4)Malloc函数的返回值是void*类型的,new的返回值某种类型指针,无需再进行类型转换

5)Malloc分配内存时,分配内存的大小需要自己计算,但new是自动计算的

3.delete和delete[ ]的区别

1)当new[]中的数组元素是基本类型时,通过delete和delete[]都可以释放数组空间;

2)当new[]中的数组元素是自定义类型时,只能通过delete[]释放数组空间。当数组中的元素是自定义类型时,delete在释放空间时只会调用数组中首个元素的析构函数,而delete[]在释放空间时会调用数组中所有元素的析构函数

 

★十、C++的引用

1.定义:

1)引用即别名,引用就是某个变量的别名,对引用的操作与对该变量本身操作完全相同

2)语法:

类型 & 引用名 = 变量名;

注:引用在定义时必须初始化,初始化以后就不能修改目标
注:引用的类型与绑定的目标变量类型一致

参考代码:

int a = 10;
int& b = a;
b++;
cout << a << endl;//11
int c = 20;
b = c; //不是让b引用c,而是将c赋值给b
cout << a << endl; //20

2.常引用

1)定义引用时加const修饰,即为常引用,不能通过常引用修改引用的目标的值

const 类型 & 引用名 = 变量;
类型 const & 引用名 = 变量;
//两种效果一样
int a = 10;
const int& b = a;//b是a的常引用
b++;//error
a++;//ok
cout << b << endl;

2)普通的引用也可以叫做左值引用,只能引用左值,而常引用也叫万能引用,既可以引用左值,也可以引用右值

3)关于左值和右值

左值(lvalue):可以放在赋值操作符左边,可以被修改

常见左值情况:

①普通变量

②前++/-- ——表达式结果是左值(++a)

③赋值表达式结果是左值(=/+=/-=......)

右值(rvalue):智能放在赋值操作符右边,不能被修改

 

常见右值情况:

①常量,字面值常量

②多数算数表达式结果都是右值(+/-/*/......)

③后++/-- ——表达式结果是右值(a++)

临时变量都是右值(隐式转换)

⑤一般函数返回的临时变量也是右值(引用型返回值是个左值)

#include <iostream>
using namespace std;
int func(void){
    int num = 10;
    cout << "&num:" << &num << endl;
    return num;//int tmp = num;
}
int main(void)
{
    //int r = tmp
    //r实际引用的函数返回的临时变量(tmp)
    const int& r = func();
    cout << r << endl;//10
    cout << "&r=" << &r << endl;

    int a = 10;
    //将a隐式转换为char类型,结果保存临时变量
    //rc实际要引用的是临时变量
    //char& rc = a;//error
    const char& rc = a;//ok
    cout << "&a=" << (void*)&a << endl;
    cout << "&rc=" << (void*)&rc << endl;

    return 0;
}

3.引用型函数参数

1)将引用用于函数的参数,这时形参就是实参的别名,可以通过形参直接修改实参,同时可以避免参数传递的过程,减小函数的调用开销,提高代码的执行效率

2)引用型参数有可能意外修改实参的值,如果不希望修改实参本身,可以将形参声明为常引用,可提高传参效率的同时还可以接收常量型的实参

参考代码:

#include <iostream>
using namespace std;
void swap1(int* x,int* y){
    *x = *x ^ *y;
    *y = *x ^ *y;
    *x = *x ^ *y;
}
void swap2(int& x,int& y ){
    x = x ^ y;
    y = x ^ y;
    x = x ^ y;
}
int main(void)
{
    int a=3,b=5;
    //swap1(&a,&b);
    swap2(a,b);
    cout << "a=" << a << ",b=" << b << endl;
}

4.引用型函数返回值

1)可以将函数的返回类型声明为引用,避免函数返回值带来的内存开销,提高代码执行效率

2)如果函数返回左值引用,那么该函数调用结果就也是一个左值

注:不要从函数中返回局部变量的引用,因为所引用的内存在函数返回以后将被释放,使用非常危险,但是可以从函数中返回成员变量、静态变量、全局变量引用

参考代码:

#include <iostream>
using namespace std;
struct A{
    int data;
    int& func(void){
        return data;
    }
    int& func2(void){
        int i=100;
        //返回局部变量引用,危险!
        return i;
    }
};
int main(void)
{
    A a = {0};
    //data = 123;
    a.func() = 123;
    cout << a.data << endl;//123
    return 0;
}

5.引用和指针

1)从C语言的角度看待引用,引用本质就是指针,但是在C++中推荐使用引用而不是指针

int i = 100;
int& ri = i;
int* const pi = &i;
ri <=等价=> pi

2)指针定义时可以不做初始化,其指向目标可以初始化后再修改(指针常量除外),而引用定义时必须初始化,且一旦初始化后,其引用目标不能在改变

 

int a,b;
int* p;//ok
p = &a;//p指向a
p = &b;//p指向b
------------------
int& r;//error
int& r = a;//r引用a,r只能是a的别名
r = b;//ok,但不是修改引用目标,仅是赋值操作

了解:

3)可以定义指针的指针(二级指针),但是不能定义引用的指针(指向引用的指针)

        int a;
	int* p = &a;
	int** pp	= &p;//ok,pp称为二级指针
	------------------------------
	int& r = a;
	int&* pr = &r;//error
	int* pr = &r;//ok,但是就是一个普通指针

4)可以定义指针的引用(指针变量的别名),但是不能定义引用的引用

	int a;
	int* p = &a;
	int*& rp = p;//ok,指针的引用
	----------------------------
	int& r = a;
	int&& rr = r;//error,C++中叫右值引用
	int& rr = r;//ok,但是就是一个普通的引用

5)可以定义指针数组,但是不能定义引用数组

	int a,b,c;
	int* parr[3] = {&a,&b,&c};//ok
	int& rarr[3] = {a,b,c};//error 

6)可以数组引用(数组的别名)

	int arr[3] = {1,2,3};
	int (&rarr)[3] = arr;//ok

7)和函数指针类似,也可以定义函数的引用(函数的别名),其语法特性和函数指针一样

	void func(int a,int b){...}
	int main(void){
		void (*pf)(int,int) = func;
		pf(100,200);//ok
		---------------------
		void (&rf)(int,int) = func;
		rf(100,200);//ok
	}

7.C++的引用和 C 语言的指针有什么区别

①值:指针指向一块内存,它的内容是指向内存的地址;引用是某变量的别名

②初始化:引用在定义的时候必须初始化,且不能为空,指针定义时可以为空

③改变:引用在定义是被初始化一次,之后不可变;指针可变

④存储:程序为指针变量分配内存区域,而引用不需要分配内存区域

⑤使用:引用使用是无需解引用,指针需解引用

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值