IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++语法基础(一) -> 正文阅读

[C++知识库]C++语法基础(一)

语法格式

头文件与宏定义

// 引入头文件
# include<头文件名>
// 宏定义
# define PI 3.1415

引用

int a = 10;
// 1、b引用a,引用必须要初始化
// int &b; // 错误
// 2、引用在初始化后,不可用再改变
int &b = a;

int c = 20;
b = c; // 这里不是更改引用,而是赋值操作

cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;

结果:
a = 20
b = 20
c = 20
可见引用操作相当于地址操作。

引用传参

引用作为函数参数传递称为引用传递,引用传递与地址传递一样会改变实参

引用作为函数的返回。

// 1、不要返回局部变量的引用
// 这个与局部变量一样,存放在栈区,会自动清除

int & test01()
{
	int a = 10; //局部变量
	return a;
}

// 2、函数的调用可以作为左值
// 静态变量,存放在全局区,全局区的数据在程序结束后由体统释放
int & test02()
{
	static int a = 10; // 静态变量,存放在全局区,全局区的数据在程序结束后由体统释放
	return a;
}

int main()
{
	int &ref2 = test02();
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;
	test02() = 1000;
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;
}

结果:
ref2 = 10
ref2 = 10
ref2 = 1000
ref2 = 1000
流程 :
ref2 引用全局变量,因此前边两个返回值为10
test02() = 1000
修改全局变量中的ref2的值为1000,main中的ref2值随之改变。
注:这里的引用是main中的ref2引用了方法中ref2,相当于别名与原名相同。

引用的本质

引用的本质在C++内部实现是一个指针常量
即指针中的常量,指向不可改,指针指向的内容可以修改。
这也是为什么引用初始化后不可以再修改的原因,而引用是可以赋值的,即对应指针常量指向的内容可以修改。

常量引用

// 常量引用,即常量指针常量,指向和内容都不可以修改
// 使用场景:用来修饰形参,防止误操作

// int a = 10;
// int & ref = 10; // 会提示错误,因为引用必须引一块合法的内存空间,常量10,在栈区
// const int & ref = 10; // 加上const之后,编译器将代码修改为int temp = 10; const int & ref = temp; 即在全局区开辟了
// 一块内存,然后引用,此时语法正确。
// 注:加入cosnt之后变为只读,不可以修改, // 引用的本质是指针常量,再加const修饰就是指向和访问内容都不可修改

// 打印数据函数,采用引用形参,指针常量
void showValue(const int &val)
{
	// 常量引用可以防止在函数体内修改了val
	cout << "val = " << val << endl;
}

int main()
{
	int a = 100;
	showValue(a);
	return 0;
}

函数提高

函数默认值

C++中函数函数是可以有默认参数的

int func(int a, int b = 20, int c = 30)
{
	return a + b + c;
}
// 如果传入了参数则使用传递的参数,如果没有缺省的,则使用默认值

注意事项

  1. 如果某个位置有了默认参数,那么这个位置往后都必须有默认值
  2. 函数声明和函数实现只能有一个有默认值,否则会导致编译器混淆默认值的使用。

函数占用参数

占用参数也可以有默认值

void func(int a, int)
{
	cout << "this if func" << endl;
}

函数重载

作用:函数名可以相同,提高复用性

函数重载满足的条件:

  1. 函数作用域相同
  2. 函数名称相同
  3. 函数参数类型,个数,或者顺序不同
    注:函数返回值不可以作为函数重载的条件

函数重载的注意事项
4. 当函数重载碰到默认参数,则可能出现二义性,会报错,尽量不使用
5. 加const和不加const可以作为函数重载的条件

类与对象

访问权限

一个类包含属性和行为,属性和行为都可以称为类的成员。

  1. public 成员类内可以访问,类外也可以访问
  2. protected 成员类内可以访问,类外不可访问,儿子可以访问父亲中的保护内容
  3. private 成员类内可以访问,类外不可访问,儿子可以访问父亲中的私有内容

C++中struct的默认权限的公共,class的默认权限是私有

构造函数和析构函数

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用

构造函数语法 类名(){}
1.构造函数没有返回值也不写void
2.函数名称与类名相同
3. 构造函数可以由参数,因此可以发生重载
4. 程序在调用时自动调用构造函数,无需手动调用,且只会被构造一次。

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
析构函数语法:~类名(){}
5. 析构函数,没有返回值也不写void
6. 函数名称与类名相同在名称前加上符号~
7. 析构函数不可以有参数,因此不能发生重载
8. 程序在对象销毁前自动调用析构函数,无需手动调用,且只会调用一次 。

构造和析构都是类必须有的方法,如果不写编译器会提供它们的空实现。

构造函数的分类和调用

按照参数分类,分为无参构造(默认构造是无参的)和有参构造。
按照类型分类,分为普通构造和拷贝构造。
拷贝构造需要将对象本身以引用的方式传递参数,且不可改变(以const修饰)

Person (const Person &p )
{
age = p.age;
}

构造函数的调用方式

  1. 括号法
    调用有参构造函数时使用,无参构造时不需要加括号,以避免编译器将调用误识别为函数的声明。
Person p1; // 无参构造
Person p2(10); // 有参构造
Person p3(p2);  // 拷贝构造
  1. 显示法
Person p1; // 无参构造
Person p2 = Person(10); // 有参构造
Person p3 = Person(p2);  // 拷贝构造
  1. 隐式转换法
Person p4 = 10;
// 相当于写了Person p4 = Person(10);

拷贝函数调用的时机

C++调用拷贝函数的时机

  1. 使用一个已经创建完毕的对象来初始化一个新对象
  2. 值传递的时候给函数参数传值(即实参向形参赋值的时候)
  3. 值的方式返回局部对象(即作为函数的返回值返回时)

构造函数的调用规则

默认情况下,C++编译器在创建一个类的时候会至少添加三个函数,默认构造,默认析构和拷贝构造。
如果用户定义了有参构造,则不会在添加默认构造,但仍然会添加拷贝构造,如果用户定义了拷贝构造,则不会添加默认构造和拷贝构造。

深拷贝和浅拷贝

深浅拷贝的区别:
1、浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝(例:assign())

2、深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝。

在代码中浅拷贝只是赋值相等,而深拷贝会自己在堆区开辟新的内存指向赋值。

在这里插入图片描述

初始化列表

class Person
{
// 传统初始化操作
// Person(int a, int b, int c)
// {
//	m_A = a;
//	m_B = b;
//	m_C = c;
//}

// 初始化列表初始化属性
Person(int a, int b, int c) :m_A(a), m_B(b), m_(c)
{
}

	int m_A;
	int m_B;
	int m_C;
}

类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为对象成员,当其他类对象作为本类成员时,先执行类对象的构造函数,然后执行类本身的构造函数。而析构正相反,先执行类本身的析构,再执行类对象成员的析构。

静态成员

静态成员就是在成员变量和成员函数前加上关键字static。

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存(全局区)
    • 类内声明,类外初始化
class Person 
{
public:
	static int m_A; // 类内声明static成员
};	

Person:: m_A = 100; // 类外初始化static成员的值

void test01()
{
Person p;
cout << p.m_A << endl; //输出100,因为初始化了
Person p2;
p2.m_A = 200;
cout << p.m_A << endl; // 输出200,因为所有对象共享同一份数据,p2将其修改为了200
}

void test02()
{
	// 静态成员变量不属于某个对象,所有对象都共享同一份数据,因此静态成员变量有两种访问方式
	//1、通过对象进行访问
	// Person p;
	// cout << p.m_A << endl;
	// 2、通过类名进行访问
	// cout<< Person::m_A << endl;
	// 两种方式是一样的。
}

注:静态成员变量也是有访问权限的

  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量

静态成员函数与静态成员变量一样既可以通过对象访问,也可以通过类名访问
静态成员函数也是有访问权限的。

成员变量和成员函数分开存储

空对象占用空间为:1
C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
每个空对象有独一无二的内存地址

  • 非静态成员变量内存分配属于类的对象上
  • 静态成员变量内存分配不属于类对象
  • 非静态成员函数内存分配也不属于类对象

this指针

每一个非静态成员函数只会诞生一份函数实例,也就说多个同类型对象会共用一块代码,C++通过提供特殊的对象指针,this指针,来区分到底是那个对象在调用函数。
this指针是隐含每一个非静态成员函数内的一种指针,this指针不需要定义,直接使用。
this指针的用途:

  • 当形参和成员变量同名时,可以用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this
  • this指针的本质是一个指针常量

const修饰成员函数

常函数:

  • 成员函数后加const后我们称这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数。
class Person
{
// 在成员函数后边加const,修饰的是this的指向,让指向的值不可以修改
void showPerson() const
{
	this->m_B = 100; // m_B添加了mutable关键字,因此可以修改
	// this->m_A =100; // 会提示语法错误因为常函数内不可修改成员属性,因为this实质是指针常量,指向不可修改,而再加了const修饰后,指向内容也不能修改。但是mutable关键字的特殊变量不受影响 
}
	int m_A;
	mutable m_B;
}

void test02()
{
	const Person p; // 在对象前加const修饰变为常对象
	// 常对象只能调用常函数,因为非常函数内可能修改成员属性,这与定义相违背。
}

友元

友元的关键字为friend
介于private与public之间,卧室属于私有空间,不对公众开放,但是对闺蜜基友开放,这就是友元

  • 全局函数作友元
class Building
{
//全局函数goodGay是Building的好朋友,可以访问私有属性
 friend void goodGay(Building *building);
public:
	Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
public:
	string m_SittingRoom; // 客厅

private:
	string m_BedRoom;	// 卧室
};
// 全局函数
void goodGay(Building *building)
{
	cout << "好基友全局函数 正在访问;" << building -> m_SittingRoom << endl; // 客厅属于公共属性,类外也能访问
	// cout << "好基友全局函数 正在访问;" << building -> m_BedRoom << endl; //卧室属于私有属性,类外不能访问
}

void test01()
{
	Building building;
	goodGay(&building);
}

本例的核心代码 friend void goodGay(Building *building);;

  • 友元做类
class Building;
class GoodGay
{

public:
	GoodGay();
	void visit(); // 参观函数 访问Building
	
	Building *building;
	
};

class Building
{
// GoodGay 是本类的好朋友可以访问本类的私有属性和方法。
friend class GoodGay;
public:
	Building();
public:
	string m_SittingRoom; // 客厅
private:
	string m_BedRoom; // 卧室
};
// 类外写成员函数
Building::Building() // 用::告诉编译器方法()属于哪个类
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}
GoodGay::GoodGay()
{
 // 创建建筑物对象
 building = new Building;
}
void GoodGay::visit()
{
	cout << "好基友类正在访问:" << building->m_SittingRoom << endl; // 公共类访问
		cout << "好基友类正在访问:" << building->m_BedRoom << endl; // 私有类得益于友元类可访问

}

void test01()
{
	GoodGay gg;
	gg.visit();
}

本例的核心代码friend class GoodGay;

  • 成员函数做友元
class Building;
class GoodGay
{
public:
	GoodGay();
	void visit(); // 让visit函数可以访问Building中的私有成员
	void visit2();// 让visit2函数不可用访问Building中的私有成员
	Building * building;
};
class Building
{
// 告诉编译器,GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员。
friend void GoodGay::visit();
public:
	Building();
public:
	string m_SittingRoom; // 客厅
private:
	string m_BedRoom; // 卧室	
	
};

// 类外实现成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

GoodGay::GoodGay()
{
	building = new Building;
}
GoodGay::visit()
{
cout << "好基友类正在访问:" << building->m_SittingRoom << endl; // 公共类访问
cout << "好基友类正在访问:" << building->m_BedRoom << endl; // 私有类访问
}
GoodGay::visit2()
{
cout << "好基友类正在访问:" << building->m_SittingRoom << endl; // 公共类访问
}

void test01()
{
GoodGay gg;
gg.visit();
gg.visit2();
}

本例的核心代码
// 告诉编译器,GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员。 friend void GoodGay::visit();

运算符重载

对已有运算符重新定义,赋予另外一种功能,以适应不同数据类型。

  • 运算符重载案例
    例如
    长方形类有两个属性长和宽,想定义长方形对象1和长方形对象2相加为把他们各自的长和宽分别相加。
class Rectangle
{
public:
	int m_width;
	int m_height;
}
Rectangle r1;
r1.m_width = 10;
r1.m_height = 10;
Rectangle r2;
r2.m_width = 10;
r2.m_height = 10;

// 可以通过自己写成员函数来实现两个对象属性相加然后返回新的对象

Rectangle RectangleAdd(Rectangle &r)
{
Rectangle temp;
temp.m_width = this->m_width + r.m_width;
temp.m_height = this->m_width + r.m_width;
return temp;
}

// 其实编译器给这种做法起了一个通用名称
operator+

Rectangle operator+(Rectangle &r)
{
Rectangle temp;
temp.m_width = this->m_width + r.m_width;
temp.m_height = this->m_width + r.m_width;
return temp;
}

当使用编译器提供的通用名称时:
Rectangle r3 = r1.operator+(r2);
可以简写为:
Rectangle r3 = r1 + r2;
当然也可以通过全局函数重载

Rectangle operator+(Rectangle &r1, Rectangle &r2)
{
Rectangle temp;
temp.m_width = r1.m_width + r2.m_width;
temp.m_height = r1.m_width + r2.m_width;
return temp;
}

运算符重载也可以发生函数重载

Rectangle operator+(Rectangle &r1,int num)
{
Rectangle temp;
temp.m_width = r1.m_width + num;
temp.m_height = r1.m_width + num;
return temp;
}

Rectangle r3 = r1 + 100;

  • 左移运算符重载即<<
    作用是可以输出自定义的数据类型
void operator<<(cout)
{

}

如果利用成员函数重载,则调用的时候:
p.operator<<(cout)简化为p<<cout,而我们正常期望的效果是 cout<<p
因此我们一般不会用成员函数来重载<<因为无法实现out在左侧。
只能利用全局函数重载左移运算符

void operator<<(cout, p) // 

本质 operator<<(cout,p) 简化为cout<<p

现在的问题是cout是什么数据类型呢?
其实他是一个ostream(输入流对象)

所以我们的重载方法应该写为

ostream & operator<<(ostream &cout,Person &p) //
{
	cout << "m_A=" << p.m_A << "m_B="  <<p.m_B;
	return cout;
} 

在使用时:
cout <<p即可输出P的属性

  • 递增运算符++重载
    功能需要包含前置递增和后置递增
// 自定义整形
class MyInteger
{
public:
	MyInteger()
	{
		m_Num = 0;
	}
private:
	int m_Num;	
}

void test01()
{
	MyInteger myint;
	cout << myint << endl;
}

// 重载<<运算符
ostream & operator<<(ostream & cout, MyInteger myint)
{
	cout << myint.m_Num;
	return cout;.
}

数据类型

数据类型取值与内存占用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

typedef声明新类型

在这里插入图片描述

enum枚举类型

在这里插入图片描述

指针

指针定义与使用

指针就是一个地址编号,代表了变量存放的地址。
X86架构,32位机所有的指针都占用4个字节
而64位机所有指针都占用8个字节

int a = 10;
int * p = &a; // &是取地址
*p = 20; // *是取值,将a修改为20

指针与数组

数组的指针是数组第一个元素存储的地址。

int [] arr = {1,2,3,4,5};
int * p = &arr;
cout << "利用指针访问第一个元素:" << *p <<endl;
p++; // 指针向后偏移
cout << "利用指针访问第二个元素:" << *p <<endl;

指针与函数

地址传递

// 值传递,形参不改变实参,即调用了该函数,只在函数内a,b交换了
void swap01(int a, int b)
{
	int temp = a;
	a = b;
	b = temp;
}
// 地址传递,可以改变实参。
void swap02(int *p1, int *p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

// 引用传递,可以改变实参。
void swap02(int &p1, int &p2)
{
	int temp = p1;
	p1 = p2;
	p2 = temp;
}

const修饰指针

int a = 10;
int b = 10;

// 1、const修饰指针,指针指向可以修改,指针指向的值不可改,称为常量的指针
int * p = &a;
const int *p1 = &a;
// *p = 20,错误
p = &b; // 指针指向可以修改

//2、const修饰常量,指针指向的内容可修改,指针指向不可改,称为指针常量
int * const p2 = &a;
*p2 = 100; //指针指向内容可改
// p2 = &b ,错误,指向不可改

//3、const既修饰指令又修饰常量,内容及指向都不可改
const int * const p3 = &a;

//技巧:将*翻译为指针,const翻译为常量,根据const int * 就是常量指针(联系:常量的指针,常量:内容不可改),int * const 就是指针常量(联系:指针中的常量,指向不可改)。const右侧紧跟着的是指针(type)*还是常量p,是指针就是常量指针,指向可改,指向的内容不可改;是常量就是指针常量,指向不可改,指向内容可改。

结构体指针

结构体以‘.’访问结构体的属性,结构体指针以->访问

struct Student
{
	string name;
	int age;
	int score;
}
struct Student studenta = {"张三",18,99};
struct Student * p = &studenta;
cout << studenta.name <<endl;
cout << p->name << endl; 

产生随机数

#include <ctime> //引入时间相关包
void main(){
	srand((unsigned int)time(NULL)); //设置随机数种子为当前系统时间,(unsigned int)强制转换
	int random = rand() % 61 + 40; // 产生0(+40)-60(+40)随机数
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:32:27  更:2022-07-03 10:35:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/11 14:48:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码