C++基础一

本文详细介绍了C++的基础知识,包括HelloWorld、双冒号运算符、命名空间、引用及其使用,强调了C++中const的特性和区别,并探讨了引用的本质与指针引用的应用。
摘要由CSDN通过智能技术生成

HelloWorld
#include <iostream>  // 标准输入输出流
using namespace std; // 使用命名空间

int main()
{
   // std::cout << "Hello World!\n";
   cout << "Hello World!" << 123<<endl; //<< 拼接符号    end line换行
   return EXIT_SUCCESS;
}
:: 双冒号运算符
int a =20;
void test(){
int a = 10;

cout << a<<endl;
cout << :: a<<endl; //全局作用域
}
namespace 命名空间
  • 解决命名冲突
  • 创建一个命名空间:
namespace A{
	int a = 10;
}
namespace B{
	int a = 20;
}
void test(){
	cout << "A::a : " << A::a << endl;
	cout << "B::a : " << B::a << endl;
}
  • 命名空间只能全局范围内定义(以下错误写法)
void test(){
	namespace A{
		int a = 10;
	}
	namespace B{
		int a = 20;
	}
	cout << "A::a : " << A::a << endl;
	cout << "B::a : " << B::a << endl;
}
  • 命名空间可嵌套命名空间
namespace A{
	int a = 10;
	namespace B{
		int a = 20;
	}
}
void test(){
	cout << "A::a : " << A::a << endl;
	cout << "A::B::a : " << A::B::a << endl;
}
  • 命名空间是开放的,即可以随时把新的成员加入已有的命名空间中
namespace A{
   int a = 10;
}

namespace A{
   void func(){
   	cout << "hello namespace!" << endl;
   }
}

void test(){
   cout << "A::a : " << A::a << endl;
   A::func();
}
  • 声明和实现可分离
#pragma once

namespace MySpace{
	void func1();
	void func2(int param);
}

void MySpace::func1(){
	cout << "MySpace::func1" << endl;
}
void MySpace::func2(int param){
	cout << "MySpace::func2 : " << param << endl;
}
  • 无名命名空间,意味着命名空间中的标识符只能在本文件内访问,相当于给这个标识符加上了static,使得其可以作为内部连接
namespace{
	
	int a = 10;
	void func(){ cout << "hello namespace" << endl; }
}
void test(){
	cout << "a : " << a << endl;
	func();
}
  • 命名空间别名
namespace veryLongName{
	
	int a = 10;
	void func(){ cout << "hello namespace" << endl; }
}

void test(){
	namespace shortName = veryLongName;
	cout << "veryLongName::a : " << shortName::a << endl;
	veryLongName::func();
	shortName::func();
}
using 的使用
  • using LOL:: sunwukongID;
  • 如果局部范围内还有 sunwukongID,会出现二义性问题,要注意避免
  • 编译指令
  • using namespace LOL
  • 如果局部范围内还有 sunwukongID ,使用局部的ID
  • 如果打开多个房间,那么也要注意二义性问题
  • using声明可使得指定的标识符可用
namespace A{
	int paramA = 20;
	int paramB = 30;
	void funcA(){ cout << "hello funcA" << endl; }
	void funcB(){ cout << "hello funcA" << endl; }
}

void test(){
	//1. 通过命名空间域运算符
	cout << A::paramA << endl;
	A::funcA();
	//2. using声明
	using A::paramA;
	using A::funcA;
	cout << paramA << endl;
	//cout << paramB << endl; //不可直接访问
	funcA();
	//3. 同名冲突
	//int paramA = 20; //相同作用域注意同名冲突
}
  • using声明碰到函数重载 ,如果命名空间包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合
namespace A{
	void func(){}
	void func(int x){}
	int  func(int x,int y){}
}
void test(){
	using A::func;
	func();
	func(10);
	func(10, 20);
}
  • using编译指令使整个命名空间标识符可用
namespace A{
	int paramA = 20;
	int paramB = 30;
	void funcA(){ cout << "hello funcA" << endl; }
	void funcB(){ cout << "hello funcB" << endl; }
}

void test01(){
	using namespace A;
	cout << paramA << endl;
	cout << paramB << endl;
	funcA();
	funcB();

	//不会产生二义性
	int paramA = 30;
	cout << paramA << endl;
}

namespace B{
	int paramA = 20;
	int paramB = 30;
	void funcA(){ cout << "hello funcA" << endl; }
	void funcB(){ cout << "hello funcB" << endl; }
}

void test02(){
	using namespace A;
	using namespace B;
	//二义性产生,不知道调用A还是B的paramA
	//cout << paramA << endl;
}

注意:使用using声明或using编译指令会增加命名冲突的可能性。也就是说,如果有名称空间,并在代码中使用作用域解析运算符,则不会出现二义性。

全局变量检测增强
  • 此代码在c++下编译失败,在c下编译通过.
int a = 10; //赋值,当做定义
int a; //没有赋值,当做声明

int main(){
   printf("a:%d\n",a);
   return EXIT_SUCCESS;
}
C++中所有的变量和函数都必须有类型
  • 以下c代码c编译器编译可通过,c++编译器无法编译通过。
//i没有写类型,可以是任意类型
int fun1(i){
	printf("%d\n", i);
	return 0;
}
//i没有写类型,可以是任意类型
int fun2(i){
	printf("%s\n", i);
	return 0;
}
//没有写参数,代表可以传任何类型的实参
int fun3(){ 
	printf("fun33333333333333333\n");
	return 0;
}

//C语言,如果函数没有参数,建议写void,代表没有参数
int fun4(void){
	printf("fun4444444444444\n");
	return 0;
}

g(){
	return 10;
}

int main(){

	fun1(10);
	fun2("abc");
	fun3(1, 2, "abc");
	printf("g = %d\n", g());

	return 0;
}
  • 在C语言中,int fun() 表示返回值为int,接受任意参数的函数,int fun(void) 表示返回值为int的无参函数。
  • 在C++ 中,int fun() 和int fun(void) 具有相同的意义,都表示返回值为int的无参函数。
更严格的类型转换
  • 以下c代码c编译器编译可通过,c++编译器无法编译通过。
typedef enum COLOR{ GREEN, RED, YELLOW } color;
int main(){

	color mycolor = GREEN;
	mycolor = 10;
	printf("mycolor:%d\n", mycolor);
	char* p = malloc(10);
	return EXIT_SUCCESS;
}
struct类型加强
  • c中定义结构体变量需要加上struct关键字,c++不需要。
  • c中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,也可以定义成员函数。
//1. 结构体中即可以定义成员变量,也可以定义成员函数
struct Student{
	string mName;
	int mAge;
	void setName(string name){ mName = name; }
	void setAge(int age){ mAge = age; }
	void showStudent(){
		cout << "Name:" << mName << " Age:" << mAge << endl;
	}
};

//2. c++中定义结构体变量不需要加struct关键字
void test01(){
	Student student;
	student.setName("John");
	student.setAge(20);
	student.showStudent();
}
“新增”bool类型关键字

标准c++的bool类型有两种内建的常量true(转换为整数1)和false(转换为整数0)表示状态。这三个名字都是关键字。

  • bool类型只有两个值,true(1值),false(0值)
  • bool类型占1个字节大小
  • 给bool类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0)
	void test()
{	cout << sizeof(false) << endl; //为1,//bool类型占一个字节大小
	bool flag = true; // c语言中没有这种类型
	flag = 100; //给bool类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0)
}

[c语言中的bool类型]
c语言中也有bool类型,在c99标准之前是没有bool关键字,c99标准已经有bool类型,包含头文件stdbool.h,就可以使用和c++一样的bool类型。

三目运算符功能增强
  • c语言三目运算表达式返回值为数据值,为右值,不能赋值。
	int a = 10;
	int b = 20;
	printf("ret:%d\n", a > b ? a : b);
	//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?
	
	//(a > b ? a : b) = 100;
	//返回的是右值
  • c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值。
	int a = 10;
	int b = 20;
	printf("ret:%d\n", a > b ? a : b);
	//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?

	cout << "b:" << b << endl;
	//返回的是左值,变量的引用
	(a > b ? a : b) = 100;//返回的是左值,变量的引用
	cout << "b:" << b << endl;

[左值和右值概念]
在c++中可以放在赋值操作符左边的是左值,可以放到赋值操作符右面的是右值。
有些变量即可以当左值,也可以当右值。
左值为Lvalue,L代表Location,表示内存可以寻址,可以赋值。
右值为Rvalue,R代表Read,就是可以知道它的值。
比如:int temp = 10; temp在内存中有地址,10没有,但是可以Read到它的值。

C/C++中的const
const概述

const单词字面意思为常数,不变的。它是c/c++中的一个关键字,是一个限定符,它用来限定一个变量不允许改变,它将一个对象转换成一个常量。

const int a = 10;
A = 100; //编译错误,const是一个常量,不可修改

C/C++中const的区别
  • C中的const
    常量的引进是在c++早期版本中,当时标准C规范正在制定。那时,尽管C委员会决定在C中引入const,但是,他们c中的const理解为”一个不能改变的普通变量”,也就是认为const应该是一个只读变量,既然是变量那么就会给const分配内存,并且在c中const是一个全局只读变量,c语言中const修饰的只读变量是外部连接的。
    如果这么写:

const int arrSize = 10;
int arr[arrSize];

看似是一件合理的编码,但是这将得出一个错误。 因为arrSize占用某块内存,所以C编译器不知道它在编译时的值是多少?

C++中的const

在c++中,一个const不必创建内存空间,而在c中,一个const总是需要一块内存空间。在c++中,是否为const常量分配内存空间依赖于如何使用。一般说来,如果一个const仅仅用来把一个名字用一个值代替(就像使用#define一样),那么该存储局空间就不必创建。
如果存储空间没有分配内存的话,在进行完数据类型检查后,为了代码更加有效,值也许会折叠到代码中。
不过,取一个const地址, 或者把它定义为extern,则会为该const创建内存空间。
在c++中,出现在所有函数之外的const作用于整个文件(也就是说它在该文件外不可见),默认为内部连接,c++中其他的标识符一般默认为外部连接。

C/C++中const异同总结
  • C语言中const是伪常量,可以通过指针修改
  • C++中const会放入到符号表中
  • C语言中const默认是外部链接,C++中const默认是内部链接
  • const分配内存情况
    • 对变量取地址,会分配临时内存
    • extern关键字下的const会分配内存
    • 用普通变量初始化const变量
    • 自定义数据类型会分配内存
尽量以const替换#define
  • define宏没有作用域概念
  • define宏常量没有类型

#define MAX 1024;
我们定义的宏MAX从未被编译器看到过,因为在预处理阶段,所有的MAX已经被替换为了1024,于是MAX并没有将其加入到符号表中。但我们使用这个常量获得一个编译错误信息时,可能会带来一些困惑,因为这个信息可能会提到1024,但是并没有提到MAX.如果MAX被定义在一个不是你写的头文件中,你可能并不知道1024代表什么,也许解决这个问题要花费很长时间。
解决办法就是用一个常量替换上面的宏。
const int max= 1024;

引用(reference)
引用基本用法
  • 注意事项:
    • &在此不是求地址运算,而是起标识作用。
    • 类型标识符是指目标变量的类型
    • 必须在声明引用变量时进行初始化。
    • 引用初始化之后不能改变。
    • 不能有NULL引用。必须确保引用是和一块合法的存储单元关联。
    • 可以建立对数组的引用。
//1. 认识引用
void test01(){

	int a = 10;
	//给变量a取一个别名b
	int& b = a;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "------------" << endl;
	//操作b就相当于操作a本身
	b = 100;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "------------" << endl;
	//一个变量可以有n个别名
	int& c = a;
	c = 200;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;
	cout << "c:" << c << endl;
	cout << "------------" << endl;
	//a,b,c的地址都是相同的
	cout << "a:" << &a << endl;
	cout << "b:" << &b << endl;
	cout << "c:" << &c << endl;
}
//2. 使用引用注意事项
void test02(){
	//1) 引用必须初始化
	//int& ref; //报错:必须初始化引用
	//2) 引用一旦初始化,不能改变引用
	int a = 10;
	int b = 20;
	int& ref = a;
	ref = b; //不能改变引用
	//3) 不能对数组建立引用
	int arr[10];
	//int& ref3[10] = arr;
}

	//1. 建立数组引用方法一
	typedef int ArrRef[10];
	int arr[10];
	ArrRef& aRef = arr;
	for (int i = 0; i < 10;i ++){
		aRef[i] = i+1;
	}
	for (int i = 0; i < 10;i++){
		cout << arr[i] << " ";
	}
	cout << endl;
	//2. 建立数组引用方法二
	int(&f)[10] = arr;
	for (int i = 0; i < 10; i++){
		f[i] = i+10;
	}
	for (int i = 0; i < 10; i++){
		cout << arr[i] << " ";
	}
	cout << endl;
函数中的引用
  • 不能返回局部变量的引用。
  • 函数当左值,必须返回引用。

最常见看见引用的地方是在函数参数和返回值中。当引用被用作函数参数的时,在函数内对任何引用的修改,将对还函数外的参数产生改变。当然,可以通过传递一个指针来做相同的事情,但引用具有更清晰的语法。

//值传递
void ValueSwap(int m,int n){
	int temp = m;
	m = n;
	n = temp;
}
//地址传递
void PointerSwap(int* m,int* n){
	int temp = *m;
	*m = *n;
	*n = temp;
}
//引用传递
void ReferenceSwap(int& m,int& n){
	int temp = m;
	m = n;
	n = temp;
}
void test(){
	int a = 10;
	int b = 20;
	//值传递
	ValueSwap(a, b);
	cout << "a:" << a << " b:" << b << endl;
	//地址传递
	PointerSwap(&a, &b);
	cout << "a:" << a << " b:" << b << endl;
	//引用传递
	ReferenceSwap(a, b);
	cout << "a:" << a << " b:" << b << endl;
}
  • 通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单:
    • 函数调用时传递的实参不必加“&”符
    • 在被调函数中不必在参数前加“*”符
  • 引用作为其它变量的别名而存在,因此在一些场合可以代替指针。C++主张用引用传递取代地址传递的方式,因为引用语法容易且不易出错。
//返回局部变量引用
int& TestFun01(){
	int a = 10; //局部变量
	return a;
}
//返回静态变量引用
int& TestFunc02(){	
	static int a = 20;
	cout << "static int a : " << a << endl;
	return a;
}
int main(){
	//不能返回局部变量的引用
	int& ret01 = TestFun01();
	//如果函数做左值,那么必须返回引用
	TestFunc02();
	TestFunc02() = 100;
	TestFunc02();

	return EXIT_SUCCESS;
}
引用的本质
  • 引用的本质在c++内部实现是一个指针常量.
    Type& ref = val; // Type* const ref = &val;
  • c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见
//发现是引用,转换为 int* const ref = &a;
void testFunc(int& ref){
   ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
   int a = 10;
   int& aRef = a; //自动转换为 int* const aRef = &a;这也能说明引用为什么必须初始化
   aRef = 20; //内部发现aRef是引用,自动帮我们转换为: *aRef = 20;
   cout << "a:" << a << endl;
   cout << "aRef:" << aRef << endl;
   testFunc(a);
   return EXIT_SUCCESS;
}
指针引用
  • 常量引用的定义格式:

const Type& ref = val;

  • 常量引用注意:
    • 字面量不能赋给引用,但是可以赋给const引用
    • const修饰的引用,不能修改。
void test01(){
	int a = 100;
	const int& aRef = a; //此时aRef就是a
	//aRef = 200; 不能通过aRef的值
	a = 100; //OK
	cout << "a:" << a << endl;
	cout << "aRef:" << aRef << endl;
}
void test02(){
	//不能把一个字面量赋给引用
	//int& ref = 100;
	//但是可以把一个字面量赋给常引用
	const int& ref = 100; //int temp = 200; const int& ret = temp;
}

[const引用使用场景]
常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数。
将函数的形参定义为常量引用的好处:

  • 引用不产生新的变量,减少形参与实参传递时的开销。
  • 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
    如果希望实参随着形参的改变而改变,那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。
//const int& param防止函数中意外修改数据
void ShowVal(const int& param){
	cout << "param:" << param << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值