首先C++兼容C语言的大多数语法,所以用C语言实现 Hello world! 同样也可以运行。 💦<1>用C语言实现 Hello world!
#include<stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
}
💦<2>用C++实现Hello world!
#include<iostream>
using namespace std;
int main()
{
cout << "Hello world!\n" << endl;
return 0;
}
namespace是为了防止变量,函数,类等的名字进行冲突。命名空间可以对标识符的名称进行本地化,从而避免了名称与名称之间的冲突,造成命名冲突或者名字污染。 🍃举例:在C语言中下面这种情况就会出现命名冲突。
#include<stdio.h>
#include<stdlib.h>
int rand = 10;
int main()
{
printf("%d\n",rand);
return 0;
}
在上面这段代码中就会出现编译报错:“ rand”:重定义;以前的定义是“函数” 原因是在stdlib.h中已经定义了rand ,而在全局中再次定义int rand = 10;就会出现命名冲突。为了解决这个问题,我们就引出了namespace.
格式:namespace + 命名空间的名字 + {} 命名空间可以定义变量\函数\类型等。 namespace的本质:namespace的本质是定义出了一个域,这个域和全局域相互独立,在不同的域里面可以定义同名的变量,但是在相同的域里面不能定义同名变量。
#include<stdio.h>
#include<stdlib.h>
namespace sp //sp是命名空间的名字,一般开发中用项目名字做命名空间名
{
//命名空间中可以定义变量、函数、类型
int rand = 10; //定义变量
int Add(int left ,int right)//定义函数
{
return left + right;
}
struct Node //定义结构体类型
{
struct Node* next;
int val;
};
}
int main()
{
printf("%p\n",rand);//这里调用的是stdlib.h中全局的rand函数指针
printf("%d\n",sp::rand);//这里的rand指的是命名空间sp中的rand
return 0;
}
📌命名空间也可以嵌套 例如:
#include<stdio.h>
#include<stdlib.h>
namespace sp
{
namespace a
{
int rand = 1;
int Add(int left, int right)
{
return left + right;
}
}
namespace b
{
int rand = 2;
int Add(int left, int right)
{
return (left + right) * 2;
}
}
}
int main()
{
printf("%d\n",sp::a::rand);
printf("%d\n", sp::a::rand);
printf("%d\n", sp::a::Add(1,2));
printf("%d\n", sp::b::Add(1,2));
return 0;
}
📌多文件中可以定义同名的namespace,他们会默认合并到一起,就如同一个namespace一样。
👀 拓展:
C++中域有函数局部域,全局域,命名空间域,类域;域影响的是函数语法查找一个变量/函数/类型出处的逻辑,所以有了域隔离,名字冲突就解决了(先局部查找,再全局查找)。局部域和全局域除了会影响语法的查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量的生命周期。
要使用命名空间中定义的变量或函数有以下三种方式: 🌿(1)指定命名空间访问,项目中最推荐的方式。
namespace N
{
int a = 1;
int b = 2;
}
int main()
{
printf("%d\n",N::a);
printf("%d\n",N::b);
return 0;
}
🌿(2)用using将命名空间中的某个成员展开,不存在命名冲突的成员推荐这种方式。
namespace N
{
int a = 1;
int b = 2;
}
using N::a;
int main()
{
printf("%d\n",a);
return 0;
}
🌿(3)展开命名空间中的全部成员,项目中不推荐这种方式,存在命名冲突的风险较大。
namespace N
{
int a = 1;
int b = 2;
}
using namespace N;
int main()
{
printf("%d\n",a);
printf("%d\n",b);
return 0;
}
< iostream>是C++中的一个标准的输入输出流库,定义了标准的输入输出对象。
#include<iostream>
std::cin是istream类的对象,面向窄字符(使用单个字节表示字符)的标准输入流。 std::cout是ostream类的对象,面向窄字符的标准输出流。 std::endl是个函数,流出入输出时,相当于一个换行符。 << 流插入运算符 , >> 流输出运算符(自动识别类型) cout/cin/endl 等都属于C++标准库,C++的标准库都存放在一个叫做std(standard)的命名空间中,所以要通过命名空间的使用方法去使用他们。
日常联系中我们可以用using namespace std; 将std库全部展开,但实际项目开发中不建议。
< iostream > 中间接的包含了<stdio.h>所以printf和scanf 在不包含<stdio.h>的情况下还能使用(在vs下)
缺省参数是定义或声明函数时给函数的参数指定一个缺省值。在调用函数是,如果没有指定实参则用缺省值,否则使用指定实参。 缺省参数又分为全缺省 和 半缺省。
全缺省:
全部参数给缺省值
半缺省:
部分参数给缺省值(c++规定半缺省必须从右往左依次连续缺省,中间不能有间断)
带缺省函数调用时,必须从右往左依次给实参,不能跳跃给实参。
📌函数定义和声明分离时,缺省参数不能在函数定义和声明中同时出现,规定必须在函数声明时给缺省值。
#include<iostream>
using namespace std;
//全缺省
void Func(int a = 10, int b = 20,int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << a << endl;
cout << "c = " << a << endl;
}
//半缺省
void Func(int a ,int b = 20 ,int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << a << endl;
cout << "c = " << a << endl;
}
int main()
{
Func1();
Func1(1);
Func1(1,2);
Func1(1,2,3);
return 0;
}
函数重载就是函数名相同而函数的形参不同,可以是参数个数不同,也可以是参数的类型不同。C++中支持同一作用域中有同名函数但C语言中不支持。
#include<iostream>
using namespace std;
//1.参数类型不同
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
//2.参数个数不同
void func()
{
cout << "func()" << endl;
}
void func(int a)
{
cout << "func(a)" << endl;
}
//3. 参数类型顺序不同(本质类型不同)
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char a, int b)
{
cout << "f(char a, int b)" << endl;
}
//!!!注意:返回值不同不能作为重载的条件,因为调用时无法区分
void fa()
{}
int fa()
{}
引用就相当于给一个变量取别名,引用不开辟新的空间,和被引用的对象公用同一块空间。 ⭐️ 格式:类型& 引用别名 = 引用对象
#include<iostream>
using namespace std;
int main()
{
int a = 10;
// b和c是a的别名
int& b = a;
int& c = a;
//也可以给别名b取别名d,相当于d也是a的别名。
int& d = b;
//其中a b c d 的地址是相同的,公用同一块空间
return 0;
}
(1)引用在定义时必须初始化 (2)一个变量可以有多个引用 (3)引用一但引用一个实体就再不能引用其他实体。(相当于我变成你的别名就不能变成其他变量的别名)
#include<iostream>
using namespace std;
int main()
{
int a = 20;
//int& b; 这里就会出现报错,原因是引用必须初始化
int& b = a;
int c = 30;
b = c;//这里并非是让b引用c,而是赋值
cout << a << endl;
cout << b << endl;
cout << c << endl;
cout << &a << endl;
cout << &a << endl;
cout << &a << endl;
return 0;
}
(1)引用传参,引用做返回值. 💧<1>可以减少拷贝提高效率. <2>改变引用对象同时改变被引用对象. (2)引用传参和指针传参的功能是类似的,引用传参更加方便。(引用不能代替指针,引用不能改变指向)
void Swap(int& a,int& b)//引用传参 // a b 引用对象 x y 被引用对象
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int x = 0,y = 1; //a 是 x的别名,b是y的别名
Swap(x,y);
//!!!注意:不能下面这样写,原因是ret是局部变量,出了作用域就被销毁了。
//所以不是所有的场景都可以用引用返回
int& func()
{
int ret = 10;
ret++;
return ret;
}
return 0;
}
C++中可以引用const对象,但必须用const引用。const引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但不能放大。
像 int& ret = a * 3这样的场景下a * 3保存在一个临时对象中;double b = 2.33, int& c = d也类似在类型转换过程中会产生临时对象来储存中间值。也就是说ret和c引用的都是临时对象,C++中规定临时对象都具有常性,所以这里就产生了权限的放大,必须得使用常引用才可以。
临时对象:
编译器需要一个空间来暂时存储表达式的求值结果时临时创建的一个未命名的对象。C++中把未命名的对象称为临时对象。
所以const引用可以引用: 1.const对象 2.普通对象 3.临时对象
#include<iostream>
using namespace std;
int main()
{
int a = 10;
//int& ra = 30; 会出现报错
const int& ra = 30;
//int& rb = a * 3;会出现报错
const int& rb = a * 3;
double d = 2.33;
//int& rd = d; 编译报错:无法从“double”类型转化为“int&”
const int& rd = d;
return 0;
}
(1)用inline修饰的函数叫做内联函数,调用时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,可以提高效率。 (2)inline对编译器而言只是一个建议,也就是说,加了inline编译器在调用时也不一定展开,不同编译器对于inline展开情况各不相同。inline适用于频繁调用短小函数,像递归函数和代码相对于多一点的函数,就算加上inline也会被编译器忽略。 (3)C语言实现宏函数时会在预处理时替换展开,但宏函数实现很复杂很容易出错,并且不方便调试,C++设计Iinline目的就是替代C语言的宏函数。 (4)inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,连接时会出现报错。
C++中NULL可能被定义为字面常量0,或者C语言中被定义为无类型指针(void*)的常量。因为C++兼容C,无论采用何种定义,在使用空指针时,就会产生分歧,到底是字面量0,还是无类型空指针(void*),因此与程序的初衷相悖。
C++中引入nullptr,nullptr是一个特殊的关键字,nullptr是一种特殊类型的字面量,它可以转化为任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转化问题,因为nullptr只能被隐式的转化为指针类型,而不能被转化为整形类型。