c++--语言基础知识

1.const
const修饰变量,表示变量不可修改,这样的变量称为常量。后续统一将没有const修饰的变量叫变量,const修饰的变量叫常量。

1.1.修饰指针时,注意区分限定的是指针还是指针指向的对象。

int a;
const int * p1 = &a;// const修饰p1指向对象,此时,允许修改p1,不允许修改*p1。p1++合法,*p1=1非法。
int * const p2 = &a;// const修改p2自身,此时,不允许修改p2,允许修改*p2。p2++不合法,*p2=1合法。

1.2.顶层const,底层const

1.2.1.区分

int a;
const int *p1 = &a;// 这里我们定义一个变量p1。此时p1是变量。p1指向的对象是常量。
int * const p2 = &a;// 这里我们定义一个变量p2。此时p1是常量。p1指向的对象是变量。

const int &q1 = a;// 这里我们定义一个引用q1,注意引用不是变量,只是给引用对象起来一个别名。 此时q1是变量a的引用。不允许通过q1修改a的值。
int & const q2 = a;// 用const修饰引用自身没有意义。引用自身本来就是初始化绑定变量后不可变更绑定的。允许通过q2修改a的值。

(1).顶层const
修饰变量自身。如上述修饰p2const
(2).底层const
修饰变量/引用指向对象。如上述修饰p1q1const

1.2.2.作用
(1).顶层const
变量执行拷贝构造,赋值时
若左边变量用顶层const修饰,右边可以是同类型变量,也可是同类型常量。
若左边变量无顶层const修饰,右边依然可以是同类型变量,也可是同类型常量。

class A
{
private:
	int m_i;
};

int main()
{
	A a;
	const A ca = a; // ok
	const A cb = ca; // ok
	A b = ca; // ok
	
	a = b;// ok
	a = ca;// ok
}

从理解的角度,拷贝或赋值时,左边和右边是两个独立的变量。右边的变量只负责提供内容。顶级const修饰与否,对变量内容无影响。

(2).底层const
指针类型变量执行拷贝构造,赋值时
若左边变量用底层const修饰,右边必须是同类型的变量或常量,是否被底层const修饰均可以。
若左边变量无底层const修饰,右边必须是同类型的变量或常量。不可被底层const修饰。

class A
{
private:
	int m_i;
};

int main()
{
	A a;
	const A ca = a;
	A* p1 = &a;// ok
	A* p2 = &ca;// err
	
	const A *cp1 = &ca;// ok
	const A* cp2 = &a;// ok
	
	p1  = &a;// ok
	p1 = &ca;// err
	cp1 = &a;// ok
	cp1 = &ca;//ok
}

引用执行初始化时,
左边被底层const修饰时,右边可以是所引用类型的变量或常量。
左边没有底层const修饰时,右边只能是所引用类型的变量。

class A
{
private:
	int m_i;
};

int main()
{
	A a;
	const A ca = a;
	A& aa1 = a;// ok
	A& aa2 = ca;// err
	const A& bb1 = a;// ok
	const A& bb2 = ca;// ok
}

1.3.可见性
c语言中被const修饰的全局变量,只在所在文件内可见。额外添加extern修饰后,变为全局可见。
c语言中被static修饰的全局变量,只在所在文件内可见。
c语言中无const,static修饰的全局变量,全局可见。

2.强制类型转换

2.1.新式
(1).static_cast:会导致精度损失的数值转换;void*到其他指针类型的转换。
(2).const_cast:用于为右边对象去掉或加上底层const属性。
(3).reinterpret_cast:非void*指针类型间转换。
(4).dynamic_cast:用于基类指针,基类引用转换为派生类指针,派生类引用。

2.2.旧式
旧式(TargetType)xx;

3.重载与作用域
同一个作用域内各个同名函数间有重载关系;
名字查找先从内层作用域依次往外层作用域查找;在当前作用域内找到名字后,停止继续查找。进入匹配阶段。

一个实例:

#include <iostream>
using namespace std;
struct Base{
    void f(double i){cout << "Base:" << i << endl;}
};
struct Derived : public Base{
    using Base::f;
    void f(int i){cout << "Derived:" << i << endl;}
};
int main(){
    Base b;
    b.f(4.5);
    Derived d;
    d.f(4.5);
    d.f(10);
    return 0;
}

在这里插入图片描述
上述执行d.f(4.5)匹配到了基类版本。这是因为我们通过在派生类里执行using Base::f;使得基类的符号f参与到从派生类作用域开始的名字查找过程。这样,执行d.f(4.5)时,通过在派生类作用域的名字查找找到两个为f的名字。接下来通过类型匹配绝对采用哪个f。这样,由于基类的f可以精确匹配,所以,最终选择基类版本。

#include <iostream>
using namespace std;
struct Base{
    void f(double i){cout << "Base:" << i << endl;}
};
struct Derived : public Base{
    //using Base::f;
    void f(int i){cout << "Derived:" << i << endl;}
};
int main(){
    Base b;
    b.f(4.5);
    Derived d;
    d.f(4.5);
    d.f(10);
    return 0;
}

在这里插入图片描述
上述执行d.f(4.5)匹配到了派生类版本。因为,执行d.f(4.5)时,通过在派生类作用域的名字查找只能找到一个为f的名字。所以,最终选择派生类版本。针对派生类执行调用时,首先在派生类搜索函数名称,若搜索到,进入匹配阶段;若搜索不到,继续到基类搜索函数名称,若搜索到,进入匹配阶段;若搜索不到,报错。

这里,额外注意两点:
(1).名字查找和类型匹配发生在编译阶段。
(2).using改变的是关联符号在作用域内的可见性。上述using使得Base::f在派生类作用域可见。默认下,Base::f在基类作用域可见。

4.默认实参。
用作默认实参的名字在函数声明所在的作用域内被解析,而这些名字的求值过程发生在函数调用时。
内联函数在编译时展开,展开函数需要定义。

5.函数匹配
5.1.确定候选函数和可行函数
候选函数:
(1).其声明在调用点可见。
(2).与被调用函数同名。

可行函数:
(1).形参数量与实参数量相等。
(2).每个实参类型和对应形参类型相同或可转换成形参类型。

5.2.寻找最佳匹配
(1).该函数的每个实参的匹配都不劣与其他可行函数需要的匹配
(2).至少有一个实参的匹配优于其他函数提供的匹配。

5.3.实参类型转换等级:
(1).精确匹配:
a.类型相同。
b.数组或函数转成指针。
c.去掉或添加顶层const
(2).非底层const实参转成底层const形参。
(3).类型提升,派生类指针转成基类指针。
(4).类类型转换

同一等级不再区分优劣。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值