类和对象中的一些琐碎的知识

类的定义:
类在C++中是由关键字 class 加上类名后面再跟上一对花括号构成的,花括号中便是类的主体,类中的东西都是类的成员,可以是变量也可以是函数。在C++中还可以用 struct 关键字来定义类,但是在C语言中,它只能被用来定义结构体。class 和 struct 的一个区别就是:class 和默认访问权限是 private 而struct 的默认访问权限是 public(因为 struct 还要兼容C 语言)

作用域:
类就形成了一个作用域,类的所有成员都在这个类的作用域里,当你要在外边定义成员的时候,就要用到 :: 这个符号来指明你定义的成员是属于哪一个类的。

实例化:
类是我们写的一个模具,并不是一个真正的实体,比如说我写了一个类叫做狗,那我在这个类里边写的就是四条腿,两只耳朵,会汪汪叫,你要惹它它可能还会咬你,你对它好它就会舔你……那符合这些就是一条狗,但是这个类本身呢?只是说明了什么是狗,所以我们得创建一个对象出来,这便是实例化,就像贵族们 new 的女朋友一样。一个类可以实例化出多个对象,实例化出的对象就会分配物理空间存储成员变量了

类创建的对象的大小:
一个类的大小实际上就是成员变量内存对其之后的大小,空类的大小为 1 ,给一个标识来记住这个类,类的大小不包括成员函数,因为每个对象都会保存一份代码,如果成员函数也要算的话,就会有太多的冗余(成员函数代码一样),所以成员函数放在了公共的代码段。

this指针:
当我们在类中写非静态成员函数的时候,有一个隐含的 this指针,就算你不写,也还是能直接用的,十分的方便,本质上是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储 this 指针。

类的 6 个默认成员函数
  1. 构造函数
  2. 析构函数
  3. 拷贝构造
  4. 赋值运算符重载
  5. 取地址操作符重载
  6. const修饰的取地址操作符重载

构造函数:
构造函数函数名和类名一样,没有返回值,是编译器自动调用的,作用是初始化对象,支持重载,你可以自己写一个构造函数,如果不写编译器也会自动生成一个,无参的构造函数和全缺省的构造函数以及自己不显示定义编译器自动生成的构造函数都是默认构造函数,默认构造函数只能有一个。编译器生成的默认的构造函数会对自定义类型成员调用它的默认构造函数。

析构函数:
对象在销毁时会自动调用析构函数,完成一些类的资源清理。
析构函数名是在类名前边加上~
一个类只有一个析构函数,如果自己不写,编译器会生成默认的析构函数
编译器自动生成的析构函数,会对自定义类型调用它的析构函数

拷贝构造:
创建一个和一个已经存在的对象一模一样的对象,只有单个形参,该形参是类类型对象的引用(一般常用const修饰)。
拷贝构造函数是构造函数的一个重载形式
拷贝构造函数只有一个参数且必须使用引用传参
没有自己写的话,编译器自动生成一个

初始化列表:
定义:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

class A{
public:
	A(int a,int b,int c)
	:_a(a)
	,_b(b)
	,_c(c)
	{}
private
	int _a;
	int _b;
	int _c;
}

每个成员变量在初始化列表中只能出现一次
类中有 引用成员变量、const成员变量、没有默认构造函数的自定义类型成员 必须放在初始化列表位置进行初始化
成员变量初始化顺序就是其在类中的声明次序

友元函数:
有这么一个问题,一个类里边的成员变量都是私有的,在类里边的函数都可以拿到,那么在类外边的函数能不能对这个私有的变量进行访问呢?能!友元函数就解决了这个问题,但是要在类的内部对这个函数进行声明,并且在声明时,前边要加 friend关键字,这样一个普通的函数就变成了友元函数,就可以对私有成员变量进行访问,同时,参数不再含有this。
注意事项: 不能能用const修饰,一个函数可以是多个类的友元函数。

友元类:
类的所有成员函数都有可以是另一个类的友元函数,可以去访问另一个类中的非公有成员
在类中声明 friend class 另一个类名

注意事项: 友元关系是单向的,友元关系不能传递

命名空间

学习C++从第一个程序 Hello World开始

// 可以写作如下样式
#include<iostream>
using namespace std;
int main() {
	cout<<"Hello world!"<<endl;
	return 0;
}
// 也可写作下面这种方式
#include<iostream>
int main() {
	std::cout<<"Hello world!"<<std::endl;
	return 0;
}

两种方式的区别在于,第一种方式用了 using namespace std,然后在打印的语句中的 cout 和 endl前边就没有写 std::。
这里的 std 便是一个名称空间标识符,也就是说是一个空间的名字,而 cout和 endl是这个空间里边的,所以要使用 cout 和 endl的话,就要使用 std::,std后边的两个冒号是作用域限定符,也就是说告诉编译器,cout 和 endl是这个std 命名空间里边的。
在C++中可以定义相同名字的变量,只要不是同一个命名空间的就可。
下边定义一个命名空间:

// 命名空间中可以是变量也可以是函数
namespace N1{
	int a = 10;
	int add(int num1,int num2) {
		return num1 + num2;
	}
}
// 命名空间中可以嵌套命名空间
namespace N2{
	int a;
	int add(int num1,int num2) {
		return num1+num2;
	}
	namespace N3{
		int b;
		int sub(int num1,int num2){
			return num1 - num2;
		}
	}
}

同时,同一个工程中允许存在多个同名命名空间,编译器最后会合并成同一个命名空间。
命名空间的使用:
命名空间有三种使用方法

// 第一种,加作用域限定符
// 例如:
int main() {
	printf("%d ",N1::a);
}
// 第二种,使用 using将命名空间中的成员引入
using N1::a;
int main() {
	printf("%d ",a);
}
// 第三种,使用namespace命名空间引入
using namespace N1;
int main() {
	printf("%d ",a);
}
缺省参数

举个栗子:

using namespace std;
void print(int a = 10){
	cout<<a<<endl;
}

在声明或定义一个函数的时候,函数的形参给定一个默认值,如果我们在调用函数的时候没有给参数,就会使用默认的参数值,也就是说,如果调用函数 print(),不给参数,最终会打印出 10。相当于说a = 10就是一个备胎,不不不,一个缺省参数…… ^ _ ^

缺省参数的分类:

  • 全缺省参数
void TestFunc(int a = 10,int b = 20,int c = 30) {
	cout<<"a = "<<a<<endl;
	cout<<"b = "<<b<<endl;
	cout<<"c" = <<c<<endl;
}
  • 半缺省参数
void TestFunc(int a,int b = 10,int c = 20) {
	cout<<"a = "<<a<<endl;
	cout<<"b = "<<b<<endl;
	cout<<"c = "<<c<<endl;
}

注意:

  1. 半缺省参数必须从右往左依次来给出,不能隔着给
  2. 缺省参数不能在函数声明和定义中同时出现(如果声明和定义同时出现,恰巧两个位置提供的值不同,那编译器就无法确定该用那个)
  3. 缺省值必须是常量或者全局变量
函数重载

定义: 函数重载是函数的一种特殊情况,C++允许在同一作用域内声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或顺序)必须不同,常用来实现功能类似数据类型不同的问题。
例如:

int Add(int left, int right) {
	return left + right;
}
double Add(double left, double right) {
	return left + right;
}
long Add(long left, long right) {
	return left + right;
}
int main() {
	Add(10, 20);
	Add(10.0, 20.0);
	Add(10L, 20L);
	return 0;
}

注意:看以下两个函数是否构成函数重载

short add(short num1,short num2) {
	return num1 + num2;
}
int add(short num1,short num2) {
	return num1 + num2;
}

不构成!!!因为函数重载并不是通过返回值的不同构成的,而是通过参数列表的不同构成的!!!

引用

引用不是定义一个变量,而是给已经存在的变量取一个别名,编译器不会为引用变量开一块内存空间,它和它引用的变量共用同一块内存空间
类型& 引用变量名(对象名) = 引用实体;

int main() {
	int a = 10;
	int& ra = a;
	// 引用类型必须和引用实体是同种类型
}

特性:

  1. 应用在定义时必须初始化、
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能应用其他实体
int main() {
	int a = 10;
	int b = 6;
	int& ra = a;
	int& rra = a;
	ra = b; // 不会引用b,只会把a的值改成6
}

常引用:

void TestConstRef()
{
const int a = 10;
//int& ra = a; // 该语句编译时会出错,a为常量
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d;
}

使用场景:

  1. 做参数
void Swap(int& x,int& y) {
	int tmp = x;
	x = y;
	y = tmp;
}
  1. 做返回值
int& Count()
{
static int n = 0;
n++;
return n;
}

注意: 如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :"<< ret <<endl;
return 0;
}

这段代码的结果是 7,因为 Add(1,2)结束后又调用了一次Add(3,4),返回值是一个引用,也就是别名,他会修改原有的结果。所以,使用引用返回不能返回局部变量。

引用和指针的区别
  1. 引用在定义时必须初始化,指针没有要求
  2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  3. 没有NULL引用,但有NULL指针
  4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全
auto关键字

auto可以推导一个变量是什么类型,但是auto定义变量时必须对变量进行初始化,然后编译器按照初始化的值得类型推导变量的类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型
使用细则:

  1. auto与指针和引用结合起来使用
    用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
  2. 在同一行定义多个变量
    当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对
    第一个类型进行推导,然后用推导出来的类型定义其他变量。
    auto不能推导的场景:
  3. auto不能作为函数参数
  4. auto不能直接用来声明数组
内联函数

以inline修饰的函数叫做内联函数,编译时 C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率

特性:

  • inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数
  • inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联
  • inline 不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

宏的优缺点:

优点:
1.增强代码的复用性。
2.提高性能。

缺点:
1.不方便调试宏。(因为预编译阶段进行了替换)
2.导致代码可读性差,可维护性差,容易误用。
3.没有类型安全的检查 。
C++有哪些技术替代宏?

  1. 常量定义 换用const
  2. 函数定义 换用内联函数
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值