栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++工程师——C/C++要点复习

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++工程师——C/C++要点复习

文章目录
  • 前言
  • C++基础入门
    • 一维数组数组名
    • 指针
      • 指针所占内存空间
      • 空指针和野指针
      • const修饰指针
      • 指针和数组
      • 指针和函数
    • 结构体
      • 结构体指针
      • 函数参数
  • C++核心编程
    • 内存4大分区
    • new操作符
    • 引用&(指针常量)
      • 引用作函数参数
    • 函数重载
    • 类与对象
      • 构造/析构函数写法
      • 深拷贝与浅拷贝
      • 列表初始化
      • 静态成员变量
      • this指针(重要)
      • 友元
      • 运算符重载
        • 加号
        • 左移
    • 继承
      • 基本语法
      • 成员函数变量同名
    • 多态
  • 参考资料

前言

为了准备秋招及春招,现阶段的任务是将所学过的知识进行复习,并串联起来。
首当其冲的就是编程语言的复习,C++自己平时写的也比较多,但仍有很多语法细节不太记得了,这里浅记一些平常容易忽略的要点。

C++基础入门 一维数组数组名

一维数组名称的用途:

  1. 可以统计整个数组在内存中的长度(单位:字节B)
  2. 可以获取数组在内存中的首地址

已知一个数组的数组名,求数组元素的个数,如下:

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "整个数组所占内存空间为: " << sizeof(arr) << endl;
cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl;
cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl;

可以通过数组名获取到数组首地址:

cout << "数组首地址为: " << (int)arr << endl;
cout << "数组中第一个元素地址为: " << (int)&arr[0] << endl;
cout << "数组中第二个元素地址为: " << (int)&arr[1] << endl;

这里首地址即为第一个元素的地址

指针 指针所占内存空间

所有指针类型在32位操作系统下是4个字节

cout << sizeof(int *) << endl; // 4
cout << sizeof(char *) << endl; // 4
cout << sizeof(float *) << endl; // 4
cout << sizeof(double *) << endl; // 4
空指针和野指针

空指针:指针变量指向内存中编号为0的空间

int * p = NULL;

用途:初始化指针变量
注意:空指针指向的内存是不可以访问的


野指针:指针变量指向非法的内存空间

int * p = (int *)0x1100;

//访问野指针报错 
cout << *p << endl;

两者共同点:都不能访问

const修饰指针

const修饰指针有三种情况:

  1. const修饰指针 — 常量指针 (const int * p)
    const修饰的是指针,指针指向可以改,指针指向的值不可以更改

  2. const修饰常量 — 指针常量 (int * const p)
    const修饰的是常量,指针指向不可以改,指针指向的值可以更改

  3. const即修饰指针,又修饰常量 (const int * const p)
    const既修饰指针又修饰常量,两者均不可修改

指针和数组

利用指针访问数组中的元素

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

int* p = arr;  //指向数组的指针

cout << "第一个元素: " << arr[0] << endl; // 1
cout << "指针访问第一个元素: " << *p << endl; // 1
cout << "指针访问第二个元素: " << *(p+1) << endl; // 2

for (int i = 0; i < 10; i++)
{
	//利用指针遍历数组
	cout << *p << endl;
	p++;
}
指针和函数

利用指针作函数参数,可以修改实参的值

// 地址传递
void swap2(int * p1, int *p2) // 声明与定义
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
swap(&a, &b);  // 调用传参

如果是数组名作为函数参数的话,两种写法效果相同,因为数组名就代表了首元素的地址

void bubbleSort(int * arr, int len)  //int * arr 也可以写为int arr[]
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}
结构体 结构体指针

通过指针访问结构体中的成员
利用操作符 -> 可以通过结构体指针访问结构体属性

student stu = { "张三",18,100};
student * p = &stu;
p->score = 80; //指针通过 -> 操作符可以访问成员
cout << "姓名:" << p->name << " 年龄:" << p->age << " 分数:" << p->score << endl;
函数参数

结构体作函数参数时,如果不想修改主函数中的数据,用值传递,反之用地址传递

// 声明
void printStudent2(student *stu);
// 调用
printStudent2(&stu);
C++核心编程 内存4大分区

C++程序在执行时,将内存大方向划分为4个区域:

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的。特点共享、只读。
  • 全局区:存放全局变量、静态变量、常量,该区域的数据在程序结束后由操作系统释放
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放内存(new),若程序员不释放(delete),程序结束时由操作系统回收
new操作符

C++中利用new操作符在堆区开辟数据
利用new创建的数据,会返回该数据对应的类型的指针

int* a = new int(10); // 创建一个值为10变量

开辟数组

int* arr = new int[10];
delete[] arr;
引用&(指针常量)

作用:给变量起别名,本质上还是原来那个变量

  • 引用必须初始化
  • 引用在初始化后,不可以改变(易错)

int a = 10;
int b = 20;
//int &c; //错误,引用必须初始化
int& c = a; //一旦初始化后,就不可以更改
c = b; //这是赋值操作,不是更改引用,a的值也会改变

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

由于c是a的别名,c在被重新赋值后,a也会随之改变
这个特点也印证了,引用本质上是一个指针常量,即指针指向的值可以修改,但是指针的指向不允许修改。

引用作函数参数

可以代替指针,实现函数内修改实参的操作

通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单

void mySwap03(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}
mySwap03(a, b);
函数重载

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

函数重载满足条件:

  1. 同一个作用域下
  2. 函数名称相同
  3. 函数参数类型、个数、顺序至少要有一个不同

注:函数返回值不可以作为函数重载条件

类与对象 构造/析构函数写法

类的定义

class Person {
public:
	Person() {
		cout << "无参构造函数!" << endl;
		mAge = 0;
	}
	Person(int age) {
		cout << "有参构造函数!" << endl;
		mAge = age;
	}
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		mAge = p.mAge;
	}
	//析构函数在释放内存之前调用
	~Person() {
		cout << "析构函数!" << endl;
	}
public:
	int mAge;
};

利用类创建对象

Person man1; // 无参构造
Person man2(100); // 有参构建
Person newman(man); // 拷贝构造函数
深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作

如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的重复释放内存空间的问题

class Person {
public:
	Person() {
		cout << "无参构造函数!" << endl;
	}
	
	Person(int age, int height) {
		cout << "有参构造函数!" << endl;
		m_age = age;
		m_height = new int(height);  // 堆区开辟
	}
	
	//拷贝构造函数  
	Person(const Person& p) {
		cout << "拷贝构造函数!" << endl;
		//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
		m_age = p.m_age;  // 非堆区开辟
		m_height = new int(*p.m_height);  // 堆区开辟

	}

	//析构函数
	~Person() {
		cout << "析构函数!" << endl;
		if (m_height != NULL)
		{
			delete m_height;
		}
	}
public:
	int m_age;
	int* m_height; // 堆区开辟
};
列表初始化

一种简化语法的操作

class Person {
public:

	传统方式初始化
	//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(c) {}
private:
	int m_A;
	int m_B;
	int m_C;
};
静态成员变量

即可以通过对象名访问,也可以通过类名访问(类名::静态成员变量名)
静态成员变量特点:

  1. 在编译阶段分配内存
  2. 类内声明,类外初始化
  3. 所有对象共享同一份数据

定义与初始化:

class Person
{
public:
	static int m_A; //静态成员变量
private:
	static int m_B; // 如果设为私有,类外也无法直接访问
};
int Person::m_A = 10; // 初始化
int Person::m_B = 10;

Person p1;
p1.m_A; // 创建对象,利用对象名调用
Person::m_A; // 直接通过类名调用
this指针(重要)

this指针在Java中也存在,不过用法上还是略有区别

this指针指向被调用的成员函数所属的对象

this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this
class Person
{
public:
	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}

	Person& PersonAddPerson(Person p)
	{
		this->age += p.age;
		//返回对象本身
		return *this;
	}

	int age;
};

// 因返回的是对象本身,所以可以连续调用
p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
友元

友元分为三种:

  • 全局函数作友元
  • 类作友元
  • 成员函数作友元

第一种最简单,直接声明前加上friend关键字,放在类的最前头即可

friend void goodGay(Building * building);

第二种需要考虑到类定义的顺序
被友元访问的那个类,需要事先声明,顺序如下:

class B;

class A
{
public:
...
private:
	B *b;
};

class B
{
	friend class A;
};

第三种成员函数作友元,与类友元类似,不过只有类中特定的一个成员函数可以访问。总体形式一样,区别在于声明语句

friend void goodGay::visit();
运算符重载

不能重载的运算符有5种,分别是.、->、sizeof、?:、::

加号
class Person {
public:
	Person() {};
	Person(int a, int b)
	{
		this->m_A = a;
		this->m_B = b;
	}
	//成员函数实现 + 号运算符重载
	Person operator+(const Person& p) {
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_B + p.m_B;
		return temp;
	}

public:
	int m_A;
	int m_B;
};

//全局函数运算符重载 可以发生函数重载 
Person operator+(const Person& p2, int val)  
{
	Person temp;
	temp.m_A = p2.m_A + val;
	temp.m_B = p2.m_B + val;
	return temp;
}

使用方法

Person p1(10, 10);
Person p2(20, 20);
Person p3 = p2 + p1;
Person p4 = p3 + 10;
左移

只能在全局函数中实现

//全局函数实现左移重载
//ostream对象只能有一个
ostream& operator<<(ostream& out, Person& p) {
	out << "a:" << p.m_A << " b:" << p.m_B;
	return out;
}

并且要作为友元,放在原类中

friend ostream& operator<<(ostream& out, Person& p);
继承 基本语法

class A : public B;

A 类称为子类 或 派生类
B 类称为父类 或 基类

class Base1
{
public: 
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

//公共继承
class Son1 :public Base1
{
public:
	void func()
	{
		m_A; //可访问 public权限
		m_B; //可访问 protected权限
		//m_C; //不可访问
	}
};
成员函数变量同名
  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
s.m_A; // 子类的变量
s.Base::m_A; // 父类的变量,加类名访问
s.func(); // 子类的函数
s.Base::func(); // 父类的函数,加类名访问
多态

未完待续……

参考资料
  1. 黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
  2. 《C++ Primer》
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1026407.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号