C++入门基础知识详解

时间:2021-10-31 09:25:59

C++的一些入门基础知识

C++入门基础知识详解

命名空间

  • 定义:命名空间是随标准C++而引入的 <=> 相当于一个更加灵活的文件域(全局域)
  • 使用:以关键字namespace开始,后接命名空间的名字
namespace JS
{
...;
}//命名空间作用域不能以分号结束

命名空间的名字在其所作用的作用域内饰唯一的
命名空间可以在全局作用域或者其他作用域内部定义,但不能在函数或者类的内部定义

  • 全局命名的冲突问题
    标准C++库中的所有组件都是在一个std的命名空间中声明和定义的。
    故,若想在平台上使用标准C++库中的组件,需要:
using namespace std;
//便可以直接使用标准C++库中的所有成员
//或者
std::成员名;

*命名空间可嵌套使用

namespace AA1
{

namespace AA2
{

int a = 10;
}
}

int main()
{
cout<<AA1::AA2::a<<endl;
}

C++基本的输入输出流

  • 在C的I/O流中,诸如printf()和scanf()函数,编译器无法检查出两函数调用的正确性。
  • 且printf()和scanf()需要知道如何输入输出已知的基本数据类型值,但C++程序中有大量的类对象,其输入输出格式是未预先定义的。
  • 所以,有了C++的I/O输入输出流类
  • C++的I/O标准输入输出流类:
    cout是标准输出流对象,<< 是输出操作符;
    cin是标准输入流对象,>>是输入操作符;
    endl是换行操作符;
    cerr是标准错误对象,<<是输出操作符;
    他们都属于C++标准库,所以都在std的名字空间里面。

函数重载

  • 什么是函数的重载?
    函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。
    重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

  • 为社么要有函数重载?
    杜绝为了同一功能的实现函数有多个名字的可能,对用户更友好。
    类的构造函数跟类名相同,也就是说:构造函数都同名。即简化实例化不同的对象。
    操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!

  • 函数重载在底层代码中的规则
    返回类型+函数名+参数列表。
    如:
// _Z12 OverloadFuncid——底层代码中的函数名
void OverloadFunc (int x, double d)
{ }
// _Z12 OverloadFuncdi——底层代码中的函数名
void OverloadFunc (double x, int d)
{ }
  • 怎样使用函数重载?
    在同一作用域类,一组函数的函数名相同,参数列表不同(个数不同/类型不
    同),返回值可同可不同。

C++为什么支持重载?
因为C和C++的函数名修饰规则不同,只要函数的参数不同,则底层代码中的函数名便不同,故C++支持函数重载。

C++的缺省参数

  • 什么是缺省参数?
    所谓缺省参数,顾名思义,就是在声明函数的某个参数的时候为之指定一个默认值,在调用该函数的时候如果采用该默认值,你就无须指定该参数。

缺省参数使用主要规则:调用时你只能从最后一个参数开始进行省略。

换句话说,如果你要省略一个参数,你必须省略它后面所有的参数,即:带缺省值的参数必须放在参数表的最后面。

缺省值必须是常量。

显然,这限制了缺省参数的数据类型,例如动态数组和界面类型的缺省参数值只能是 nil;至于记录类型,则根本不能用作缺省参数。 缺省参数必须通过值参或常参传递。

  • 怎么使用缺省参数
// 全缺省参数
int Add1 (int a = 0, int b = 0)
{
return a + b;
}
// 半缺省参数
int Add2 (int a, int b = 0)
{
return a + b;
}
void Test ()
{
Add1();
Add1(1);
Add1(1,1);
Add2(2);
Add2(2,2);
}

指针和引用

指针

引用

  • 什么是引用?
    • 引用是个别名,当建立引用时,程序用另一个变量或对象的名字初始化它。
    • 引用作为目标的别名而是要,对引用的改动实际就是对目标的改动。
  • 如何使用引用?
    用运算符“&”来表示引用

引用不是值,不占存储空间,声明引用时,目标的存储状态不会改变。所以,引用只有声明,没有定义。

可以使用引用来初始化。

int main()
{
int iOne;
int& rOne = iOne;

iOne = 7;
cout<<"iOne ="<<iOne<<endl;
cout<<"rOne ="<<rOne<<endl;

rOne = 5;
cout<<"iOne ="<<iOne<<endl;
cout<<"rOne ="<<rOne<<endl;
}

运行结果为:

C++入门基础知识详解

  • 引用的操作
    引用在建立时就初始化,而且总是作为目标的别名使用,即使在应用地址操作符时也如此。
    引用一旦初始化,它就维系在一定的目标上,再也不分开。任何对该引用的赋值,都是对引用所维系的目标赋值,而不是将引用维系到另一个目标上。
int main()
{
int iOne;
int& rOne = iOne;
int iTwo = 8;

iOne = 5;
cout<<"iOne ="<<iOne<<endl;
cout<<"rOne ="<<rOne<<endl;

cout<<"&iOne ="<<&iOne<<endl;
cout<<"&rOne ="<<&rOne<<endl;

rOne = iTwo;
cout<<"iOne ="<<iOne<<endl;
cout<<"rOne ="<<rOne<<endl;
cout<<"iTwo ="<<iTwo<<endl;

cout<<"&iOne ="<<&iOne<<endl;
cout<<"&rOne ="<<&rOne<<endl;
cout<<"&iTwo ="<<&iTwo<<endl;
}

运行结果为:

C++入门基础知识详解

由图可见,rOne和iOne的地址是没有改变的。

  • 为什么能被引用?

    若一个变量声明为T&,即引用时,它必须用T类型的变量或对象,或能够转换成T类型的对象进行初始化。
    如果引用类型T的初始值不是一个左值,那么将建立一个T类型的目标并用初始值初始化,那个目标的地址变成引用的值。

    • 指针也是变量,所以可以有指针变量的引用。
    • 对void进行引用是不允许的。
    • 不能建立引用的数组。
      • 因为数组是某个数据类型元素的集合,数组名表示该元素集合空间的起始地址,它自己并不是一个名副其实的数据类型。
    • 引用不是类型,所以没有引用的引用。
    • 引用不能用类型来初始化。
      • 因为引用是变量或对象的引用,而不是类型的引用。
    • 有空指针,无空引用。
  • 用引用传递函数参数
    传递引用给函数与传递指针的效果一样,传递的是原来的变量或对象,而不是在函数作用域内建立变量或对象的副本。

函数示例:

void swap(int & r1,int & r2)
{
int tmp = r1;
r1 = r2;
r2 = tmp;
}

int main()
{
int x = 8;
int y = 6;

cout<<"before "<<"x = "<<x<<" y = "<<y<<endl;

swap(x,y);

cout<<"after "<<"x = "<<x<<" y = "<<y<<endl;
}

运行结果如下:

C++入门基础知识详解

引用传递的内存布局与指针相仿,只是操作完全不同。
每当使用引用时,C++就去求该引用所含地址中的变量值。

  • 引用传参可能会存在的问题

    • 在无法看到函数原型的时候,会误认函数是通过实参来进行值传递的。
    • 即,引用隐藏了函数所引用的参数传递的类型,所以无法从所看到的函数调用判断是值传递还是引用传递。
    • 在代码内,若使用了重载函数,可能会因编译器无法识别是值传递还是引用传递而引起编译器的报错。
  • 返回多个值
    函数只能返回一个值。
    可以用引用给函数传递两个参数,然后由函数往目标中填入正确的值。

具体函数实现:

int Factor(int n, int& rSquard, int& rCubed)
{
if(n>20 || n<0)
return 1;
rSquard = n*n;
rCubed = n*n*n;
return 0;//只使用了一个返回值
}

int main()
{
int error, Squard, Cubed,n;

cout<<"please enter number n(0~20)"<<endl;
cin>>n;

error = Factor(n,Squard,Cubed);

if(error)
cout<<"ERROR!";

else
{
cout<<"number = "<<n<<endl;
cout<<"Squard = "<<Squard<<endl;
cout<<"Cubed = "<<Cubed<<endl;
}
}

程序所需要的其他两个值是通过改变传递给函数的引用返回的,而没有使用函数返回值。

代码实现如下:

C++入门基础知识详解

  • 用引用返回值
    函数返回值时,要生成一个值的副本。
    而在用引用返回值时,不生成值得副本。

以下函数关于引用返回的四种形式:

float temp;

float Circle(float r)
{
temp = r*r*3.14;
return temp;
}

float& rCircle(float r)
{
temp = r*r*3.14;
return temp;
}

int main()
{
float a = Circle(3.0);
float& b = Circle(3.0);
float c = rCircle(3.0);
float& d = rCircle(3.0);

cout<<a<<endl;
cout<<b<<endl;
cout<<c<<endl;
cout<<d<<endl;
}

四种方式按道理来说,输出的值应该都是相同的。

  • 第一种
    • 一般的函数返回值方式。
    • 返回全局变量temp时,会创建临时变量将temp的值复制给该临时变量。
  • 第二种
    • 编译器会报错。
    • 以值方式返回,返回时,复制temp的值给临时变量。返回到主函数时,引用b以该临时变量来初始化,使b成为该临时变量的别名。
    • 但临时变量的作用域短暂,b面临无效的危险。因为临时变量或对象的生命期在一个完整的语句表达式结束后便宣告结束,故引用b的值是一个无法确定的值。
  • 第三种
    • 最佳的返回方式
    • 函数返回值不产生副本,直接将变量temp返回给主函数。赋值语句中,c直接从temp得到复制,避免了临时变量的产生。
    • 程序执行效率高,空间利用的利益高。
  • 第四种
    • 函数返回一个引用,不产生返回值的副本。
    • 在主函数中,使用一个引用声明d用该返回值初始化,使d成为temp的别名。
    • 由于temp是全局变量,所以在d的有效期内,temp始终保持有效,
    • 但,如果返回了一个不再作用域范围内的变量或对象的引用,是个很严重的错误。

引用和指针的区别

  • 指针是个变量,可以把它再赋值成指向别处的地址
  • 建立引用是必须进行初始化并且绝不会再指向其他不同的变量