C++知识点(持续更新...)

目录

C++基础

输入输出

 bool类型

new操作符

new和malloc的区别

malloc分配内存失败的情况:

引用

函数参数默认值

函数重载

函数重载碰到函数默认参数

类和对象

 封装

类的构成--三种权限

类和结构体的区别



C++基础

输入输出

#include<iostream>
//cin cout 从内存中输入输出 分别是 istream类型 ostream类型
//c++的头文件库包含c语言的头文件库 可以写scanf和printf
using namespace std;
//namespace命名空间 std-标准
//使用标准命名空间 限制cin/cout的使用范围在std下
namespace S
{
	int a;
}
int a, b;//全局
int main()
{
    //cin 从控制台读入内容到变量。 cout从变量输出内容到控制台。
	cin >>a >> b;
	cout << a <<" " << b<<endl;//endl--换行
	S::a = 4;//S是作用域
	cout << a << " " << b << endl;
	cout << S::a << " " << b << endl;
	return 0;
}


 bool类型

  • bool类型 :是c++中一种整型类型   只占1字节 值仅有两个  false(0)和true(1)
  • 非零的正负数都是1(false)

为什么只占一个字节?   

答:1字节等于8位长的数据单位,大多数的计算机用1个字节表示1个字符、数字或其他字符,故1字节足够表示两个值了

#include<iostream>
using namespace std;

int main()
{
	bool a1 = -1 ;    //将被转换为true,非零正负值都转换为true。
	bool a2 = 0;      //将被转换为false
	int b1 = true;    //将被转换为1
	int b2 = false;   //将被转换为0
	
   cout << a1 << endl;
	cout << a2 << endl;
	cout << b1 << endl;
	cout << b2 << endl;
return 0;
}

new操作符

  • 是C++中在堆区开辟数据的
  • 语法:new+数据类型 
  • 利用new创建的数据会返回数据对应类型的指针
  • 在堆区申请内存,释放时用delete 释放数组是用delete[]
  • new申请数组时返回首元素的地址,申请int型变量返回的是这块地址
int main()
{
	int* a1 = new int;//在堆区申请一块内存为int类型的内存 随机值
	cout << a1 << endl;
	delete a1;

	int* a2 = new int();//在堆区申请一块内存为int类型的内存 值为0
	cout << a2 << endl;
	delete a2;

	int* a3 = new int(4);//在堆区申请一块内存为int类型的内存 4
	cout << a3 << endl;
	delete a3;

	int* a4 = new int [3];
	for (int i = 0; i <3; i++) 
        cout << a4[i]<<" ";//在堆区申请一块内存为int类型的数组 值随机值
	delete[]a4;//释放时不加[]会发生 内存泄漏(这块内存丢了,找不到)

	int* a5 = new int[3] {1, 2, 3};//在堆区申请int类型数组 值为1 2 3
	cout << a5;
	delete[]a5;
    cout<<a5;//报错,释放的空间无法找到并访问
	return 0;
}
new和malloc的区别

new是在堆区申请内存的,如果给类或者结构体申请内存的话会优先调用malloc 再调用构造函数,
释放new申请的内存需要使用delete,释放数组需要delete[],delete会先调用析构函数在调用free,
当这个类的析构函数没有作用时,也可以使用free释放new申请的堆区空间

  • 1.返回值 new返回值不需要强转 malloc返回值需要强转‘
  • 2.名字:new是运算符可以重载 malloc是c语言库函数不可以重载
  • 3.参数:new不需要传入具体字节个数 malloc需要
  • 4.函数体:new先调用malloc再调用构造函数给成员变量赋值 delete先调用析构在调用free,malloc只分配堆区内存
  • 5.申请内存失败:new会抛出异常 malloc返回空
malloc分配内存失败的情况:

(1) 参数不正确

int*p =(int *)malloc(-12);//不能为负数

(2) 堆区没足够的空间

引用

1.性质 

  • 作用是给变量起别名,引用必须初始化,且初始化不能为空,引用不能改变引用关系(底层是指针常量type *const pointer)),指针常量不可修改
  • 引用可以作为函数的参数和函数返回值存在,但是引用不能接收局部变量  加static可接收
  • 引用不占内存,函数传参时,利用引用的技术让实参和形参代表的是同一块内存空间
  • 引用分为:左值引用,右值引用,万能引用(在左值引用前加const)。左值引用只能接收左值,右值引用只能接收右值,万能引用都能接收
  • 左值是有名字有地址的变量,右值是无名字无地址的变量。匿名对象(只存在当前行,下一行就被释放)和常量都是右值

       【注】 const修饰形参,防止形参改变实参

【问】在C++中引用不能返回局部变量的的原因有:

  1. 局部变量的生命周期在函数返回后结束。
  2. 但是引用的生命周期会延续到调用的上下文环境中。
  3. 这样就出现了一个引用指向的内存已经被释放的非法引用。使用这个无效的引用可能会导致程序崩溃或其他难以预料的后果。

2.参数传递有两种方式 

  •         值传递
void test01(int a, int b)//值传递不修改原参数的值
{
	int c = a;
	a = b;
	b = c;
}
  •         引用传递
void test02(int &a, int &b)
{
	int c = a;
	a = b;
	b = c;
}

        左值引用右值引用

#include<iostream>
using namespace std;
//返回局部变量引用
int& test01() {
	int a = 10; //局部变量
	return a;
}

//返回静态变量引用
int& test02() {
	static int a = 20;
	return a;
}

int main() {

	//不能返回局部变量的引用
	int& ref = test01();
	cout << "ref = " << ref << endl;
	cout << "ref = " << ref << endl;

	/*有名字的变量是左值例如:a = 1 ,a为左值,例如: 2 ,2 是右值
	左值引用只能接收左值,右值引用只能接收右值*/
	int&& R = 2;//右值引用接收右值
	int a = 2;
	int& R1 = a; //左值引用只能接收左值

	//int &&R2 = a; //报错
	//int &R3 = 2; //报错

	//万能引用:前面用const 修饰
	const int& R4 = 2;
	const int&& R5 = 3;

	//如果函数做左值,那么必须返回引用
	int& ref2 = test02();
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;

	test02() = 1000;

	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;

	return 0;
}

3.匿名对象(右值) 

  • 没有名字的对象
  • 只存在于当前行,到下一行就被释放掉了
  • 函数以引用方式返回的值是左值 不引用返回的是匿名对象

4.引用和指针 

  • 都可以修改目标对象的值
  • 都可以用于传递函数参数
  • 引用必须在生命时初始化,指针可以随时初始化

函数参数默认值

c++中参数的形参列表中的形参是可以有默认值的

必须是从右向左依次赋默认值

函数在主函数下面定义需要声明 声明时给默认值,实现时不需要给了

//void fun(int a = 1, int b = 2, int c = 3)
//{
//	cout << a << b << c << endl;;
//}

//函数在主函数下面定义需要声明 声明时给默认值,实现时不需要给
void fun(int a = 1, int b = 2, int c = 3);

int main()
{
	fun(1);
	fun(1, 22);
	fun(1, 22, 333);
	return 0;
}

void fun(int a, int b, int c)
{
	cout << a << b << c << endl;;
}
函数重载

1.定义

        在同一个作用域下,函数名字相同,参数类型 或 个数 或 顺序不同

【注】:函数的返回值不作为函数重载的条件

void fun() { cout << "无参" << endl; }
void fun(int a) { cout << "int" << endl; }
void fun(int a, double b) { cout << "int double" << endl; }
void fun(double a, int b) { cout << "double int" << endl; }

int main()
{
	fun();
	fun(2, 1.0);
	fun(5);
	fun(1.8, 2);
}

2.c语言为什么不能重载而c++可以? 

C语言和c++的编译方式不同,c++中编译器编译后函数名是由原函数名+参数类型构成 ,参数不同就是函数名不同,调用时不会发生冲突

而C语言编译方式起完的名字还是自己 不能重载会发生冲突 

3.函数重载的调用时期

函数在调用时 是在编译期间确定调用哪个重载函数的 ,根据实参类型确定的。编译器会选择一个最合适的函数来执行

4.extern "C" 声明全局变量函数

函数重载碰到函数默认参数

        在一个类中,当无参数的构造函数和带默认参数的构造函数重载时,有可能产生二义性

//1.引用作为传递条件

void func(int& a)
{
	cout << "func(int a)调用" << endl;
}
void func(const int& a)
{
	cout << "func(const int &a)调用" << endl;
}

//2.函数重载碰到函数默认参数

void func2(int a, int b = 10)
{
	cout << "fun2(int a,int b=10)调用" << endl;
}

void fun2(int a)
{
	cout << "fun2(int a)调用" << endl;
}
int main()
{
	int a;
	func(a);//调用无const
	func(10);//调用有const

	func2(10);
	return 0;
}

类和对象

1.面向对象三大特征 封装 继承 多态

  c++-->万物皆对象       对象:凡是占有内存的      int a a是对象

2.属性是成员变量 行为是成员函数或者成员方法

3.在定义对象时,若定义的是指向此对象的指针变量,则访问此对象的成员时,不能用“.”操作符,而应该使用“->“操作符。如

 封装

意义:

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

 【注】类内声明 类外实现时要加作用域

            空类大小是1

设计一个类求圆的周长

class Circle
{
public:
	double r;
	void area()
	{
		cout << 2 * 3.14 * r;
	}
};
int main()
{
	Circle c;
	c.r = 5.8;
	c.area();
	return 0;
}

设计一个类显示学生信息 

#include<string>
class Student
{
public:
	string name;
	int sid;
	void set_name(string n)
	{
		name = n;
	}
	//类内声明 
	void card();
};
//类外实现 要加作用域
void Student::card()
{
	cout << "姓名:" << name << " " << "学号:" << sid;
}
int main()
{
	Student s1;
	s1.set_name("张三");
	s1.sid = 202305;
	s1.card();
}

类的构成--三种权限

public 公有权限 类内 子类 对象可以访问

protected 受保护权限 类内和子类能访问

private 私有权限 只有类内能访

【注】数据设置为私有 为了保证数据的准确性,提高安全性

#include<iostream>
using namespace std;
#include<string>

class Person
{
public://公有权限  类内 子类 对象可以访问
	string name;
	//name = "123";//没有申请空间不能访问
protected://受保护权限 类内和子类能访问
	string car;
private://私有权限 类内能访问
	string password;
public:

	void set_car(string c)
	{
		car = c;
	}
	void set_password(string s)
	{
		password = s;
	}
	void printf()
	{
		cout << name << car << password;
	}
	void fun()
	{
		name = "张三";
		car = "红旗";
		password = "123654";
	}
};
int main()
{
	//创建对象时申请内存  给成员变量分配内存
	Person p;
	p.name = "李四";
	p.fun();
	p.set_car("劳斯莱斯");
	p.set_password("666");
	p.printf();
	//p.car = "拖拉机";//受保护权限不能调用
}
类和结构体的区别

【问】c++中类和结构体的唯一区别?

        默认的访问权限和继承权限不同,类的默认访问权限是私有的结构体是公有的

#include<iostream>
using namespace std;
#include <iomanip>
#include<cmath>
class C1//数据设置为私有  为了保证数据的准确性,提高安全
{
	int m_A;//默认是私有权限
};
struct C2
{
	int m_a;//默认是公共权限
};
int mian()
{
	C1 c1;
	c1.m_A = 10;//错误,访问权限是私有的

	C2 c2;
	c2.m_A = 10;//正确,访问权限是公共

	system("pause");
}

对象的初始化和清理

构造函数和析构函数
        构造函数

        语法:类名+(){}

  编译器提供的默认构造函数是空实现        

  构造函数可以是私有的

1.当写一个类时 c++的编译器会提供4个默认函数构造函数 析构函数 浅拷贝 赋值运算符

2.构造函数是一种特殊的成员函数

  • 主要作用是在创建对象时用来给成员变量赋值 ,名字必须与类名相同 ,可以有任意类型的参数可以重载,但是不能有返回值
  • 在创建对象的时候编译器自动调用构造函数 且只调用一次,如果没有实现构造函数编译器会提供默认构造函数

        一个类的指针对象,如果分配空间的话,就会调用构造函数,在析构时要手动调用delete 如果没有分配就,不会调用。

#include<iostream>
using namespace std;
#include<string>
#include<iomanip>

class Pointer
{
	int* p;
	int a, b;
public:
	Pointer()//构造函数没有返回值 //如果没有实现构造函数,编译器会提供一个默认的构造函数
	{
		//赋值:给已存在的变量一个值
		//初始化:再创建一个变量时给他一个值
		b = a;
		a = 1;
		cout << "无参构造" << a << b;;
	}
	Pointer(int n)
	{
		if (n > 0) p = new int[n];//在堆区申请n个大小为int类型的变量
		cout << "有参构造" << endl;
	}
};

int main()
{
	Pointer p1;//在创建对象时编译器自动调用
	Pointer p2(2);//调用有参构造 创建对象
	Pointer p3();//编译器会认为是函数声明 Pointer是返回值类型 p3是函数名 ()是参数
}

 构造函数的三种调用方式

int main()
{
	//构造函数的分类调用
	/*
	括号法 显示法 隐式转换法(构造函数前面加explicit可以屏蔽隐式转换)
	*/
	Pointer a();//括号法

	//显示法
	Pointer b = Pointer(2);

	//隐式转换法
	Pointer c = 2;//将int类型通过构造函数 隐式转换成Pointer类型 

}
        析构函数

 性质:

  • 是用来释放成员变量指向的堆区内存的 名字和类名相同前面加~
  • 没有参数和返回值 不能重载
  • 在释放对象时编译器自动调用构造函数,且只调用一次如果没有实现析构函数编译器会提供默认的析构函数
class Pointer
{
	int n;
	int* p;
public:
	Pointer()
	{

	}
	Pointer(int n)
	{
		this->n = n;
		p = new int[n];
	}
	~Pointer()
	{
		if (p)delete[]p;
	}
};


int main()
{
	Pointer p(4);//调用无参构造创建A类型的对象在堆区
Pointer* p1 = new Pointer;//无参
delete p1;
Pointer* p4 = new Pointer[3]{ Pointer(1),Pointer(2) };
delete[]p4;
}

delete先调用析构(内存空间)再调用free(free释放p自己)

new先调用malloc再调用构造函数

【注】当对象的生命周期结束时,析构函数会被自动调用

拷贝构造函数

定义:形参是本类对象的引用,通过已存在的对象初始化新的对象

拷贝构造参数必须是引用避免递归加const避免修改实参

常见的用处(调用时机):

  • 对象以值的方式作为函数参数
  • 对象以值的方式作为函数返回值
  • 使用已存在的对象初始化新的对象

如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
如果用户定义了拷贝构造,编译器不会提供其他构造函数

  • 每个类都必须有一个拷贝构造函数。可以自己定义拷贝构造函数,用于按照需要初始化新对象;如果没有定义类的拷贝构造函数,系统就会自动生成一个默认拷贝构造函数,用于复制出与数据成员值完全相同的新对象。

编译器会提供默认的拷贝构造 浅拷贝 构造 拷贝构造 析构函数

#include<iostream>
using namespace std;

class A
{
public:
	A() {};
	A(const A& other)
	{
		cout << "拷贝构造" << endl;
	}
};
A fun()
{
	A a;//调用无参构造
	return a;
}
void fun(A a)
{

}

int main()
{
	fun();
	A a;
	fun(a);
	A b(a);//已存在的对象初始化新的对象
	A c = a;//隐式类型转换调用构造函数
}
#include<iostream>
using namespace std;

class Person
{
public:
	int age;
public:
	//无参构造
	Person()
	{
		cout << "无参构造!" << endl;
	}

	//有参构造
	Person(int a)
	{
		age = a;
		cout << "有参构造" << endl;
	}

	//拷贝构造函数 参数为引用 避免递归 加const避免修改实参
    //构造函数拷贝自己这种东西叫拷贝构造
	Person(const Person& p)
	{
		age = p.age;
		cout << "拷贝构造" << endl;
	}

	//析构函数
	~Person()
	{
		cout << "析构函数!" << endl;
	}
};
void test01()
{
	Person p1(18);
	//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
	Person p2(p1);

	cout << "p2的年龄为: " << p2.age << endl;
}

void test02()
{
	//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
	Person p1; //此时如果用户自己没有提供默认构造,会出错
	Person p2(10); //用户提供的有参
	Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供

	//如果用户提供拷贝构造,编译器不会提供其他构造函数
	Person p4; //此时如果用户自己没有提供默认构造,会出错
	Person p5(10); //此时如果用户自己没有提供有参,会出错
	Person p6(p5); //用户自己提供拷贝构造
}

int main() {

	test01();
	return 0;
}

拷贝构造函数是通过已经存在的对象初始化新对象

1.什么时候会调用拷贝构造?

参数或返回值以值的方式传递时

2.参数是const A&other 万能引用,左值右值都可以接收

3.参数含有指针时用深拷贝,浅拷贝会导致内存泄漏,两个变量指向同一个位置,修改一个另一个也改变

深浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区申请新内存,进行拷贝操作

#include<iostream>
using namespace std;

class Person
{
public:
	int age;
	int* m_height = nullptr;
public:
	//无参构造
	Person()
	{
		cout << "无参构造!" << endl;
	}

	//有参构造
	Person(int a,int height)
	{
		this->age = a;
		this->m_height = new int(height);
		cout << "有参构造" << endl;
	}

	//拷贝构造函数 参数为引用 避免递归 加const避免修改实参
	Person(const Person& p)
	{
		age = p.age;
		this->m_height = p.m_height;//浅拷贝
		this->m_height = new int(*p.m_height);//深拷贝
		cout << "拷贝构造" << endl;
	}

	//析构函数
	~Person()
	{
		cout << "析构函数!" << endl;
		if (m_height)
			delete m_height;
	}
};

int main() {
	//编译器会提供默认的拷贝构造 浅拷贝 析构函数
	//拷贝构造就是简单的赋值
	//浅拷贝:不同对象里面的指针类型成员变量指向同一块堆区内存,会造成一块内存
	//多次释放编译器会报错,其中一个对象修改了内存另一个对象也会被修改
	Person p(16, 4);
	Person p1 = p;
	return 0;
}
#include<iostream>
#include<vector>
#include<string>
using namespace std;

class Person
{
	int age;
	int *m_height = nullptr;
	string name;
public:
	Person() { cout << "无参默认构造" << endl; }
	Person(int age,string name,int height)
	{
		this->age = age;
		this->name = name;
		this->m_height = new int(height);
	}
	//参数为引用 避免递归,加const 避免修改实参
	Person(const Person& other)
	{
		this->age = other.age;
		this->name = other.name;
		//this->m_height = other.m_height;  浅拷贝
        //会导致两个对象里的两个成员变量指向同一个堆区内存,
        //一个对象修改,另一个也随之修改

		//下面是深拷贝
		this->m_height = new int(*other.m_height);
	}
	~Person()
	{
		cout << "析构函数" << endl;
		if (m_height)
		{
			delete m_height;
		}
	}
};

int main()
{
	Person p(16,"张三",4);
	Person p1 = p;
}
初始化参数列表

     初始化参数列表只能在构造函数中使用,是给成员变量初始化的,初始化的顺序和参数列表的顺序无关,和成员变量的顺序一致(成员变量顺序决定初始化顺序),引用和常量在初始化参数列表中初始化

#include<iostream>
using namespace std;
class Person
{
	int P_a, P_b, P_c;
	int& P_e;
	const int P_f;
public:
	Person(int a, int b, int c) :P_a(c), P_b(P_c), P_c(c),P_e(a), P_f(b){};
	void print()
	{
		cout << P_a << " " << P_b << " " << P_c << " " << P_e << " " << P_f;
	}
};

int main()
{
	Person p(1,2,30);
	p.print();
	return 0;
}
对象成员

类中的成员是其他一个类的对象,称该成员为 对象成员

构造顺序:对象成员的构造,在调用本类构造

析构顺序:与构造顺序相反

#include<iostream>
using namespace std;
const int N = 1e4 + 5;//数组的大小需要用常量定义
int dt[N][N];

class A
{
public:
	A(int a)
	{
		cout << "A的构造" << endl;
	}
	~A()
	{
		cout << "A的析构" << endl;
	}
};

class B
{
    //没有无参构造,所以创建对象a要给个参数,在初始化参数列表初始化
	A a;//构造函数是给对象初始化的,是给成员变量赋值的
public:
	B(int b):a(b)
	{
		cout << "B的构造" << endl;
	}
	~B()
	{
		cout << "B的析构" << endl;
	}
};
int main()
{
	B b(2);
}
静态成员函数

特点:

  • 所有对象共享一个静态成员变量
  • 在编译阶段,主函数之前进行初始化
  • 在类内声明 类外初始化
  • 可以使用类名或对象名访问公有的静态成员变量
  • 在发生继承时,静态成员变量不会被继承,父类子类共享同一个静态成员
  • 静态成员变量不占对象的内存(静态成员变量在静态区,所有对象共同访问同一个)

静态成员变量在静态区,静态成员函数在代码区

  1. 静态数据成员的定义与普通数据成员相似,但前面要加上static关键字。
  2. 静态数据成员的初始化与普通数据成员不同。静态数据成员初始化应在类外单独进行,而且应在定义对象之前进行。一般在main()函数之前、类声明之后的特殊地带为它提供定义和初始化。
  3. 静态数据成员属于类(准确地说,是属于类中对象的集合),而不像普通数据成员那样属于某一对象,因此,可以使用“类名::”访问静态的数据成员。格式如下:类名::静态数据成员名。
  4. 静态数据成员与静态变量一样,是在编译时创建并初始化。它在该类的任何对象被建立之前就存在。因此,共有的静态数据成员可以在对象定义之前被访问。对象定以后,共有的静态数据成员也可以通过对象进行访问。
#include<iostream>
using namespace std;
class B
{
public:
	B()
	{
		cout << "B的构造" << endl;
	}
};

class A
{
public:
	static int a;//类内声明 类外初始化
	static B c;
	int b = 0;
	A()
	{
		a++;
		b++;//所有对象共享同一个静态成员变量
	}
};
int A::a = 0;
B A::c = B();
int main()
{
	A a, b, c, d;
	cout << a.a <<" " << b.a <<" " << c.a <<" " << d.a << endl;
	cout << sizeof(a) << endl;
	A::a = 1;
}
空指针访问成员函数
#include<iostream>
#include<algorithm>
using namespace std;

class A {
    int a;
public:
    void fun()
    {
        //this->a=3;
        cout << "work" << endl;
    }
};

int main()
{
    //空指针可调用函数,但是函数中有静态成员变量时会报错
    //因为this是空指针
    A *a1=nullptr;
    a1->fun();
    return 0;
}

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

了一li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值