C++基础-类、对象

类定义

C++11中,基本类型成员变量可以在类中声明时给默认值。

  • 声明定义都在类中

成员函数如果在类中定义,编译器可能会默认将其当成内联函数处理。

cpp 复制代码
class Person
{	
public:
	void showInfo() {
		cout << _name << "-" << _age << endl;
	}
private:
	char* _name;
	int _age;
};
  • 类声明在.h文件中,成员函数定义在.cpp文件中.

person.h:

cpp 复制代码
class Person
{	
public:
	void showInfo();
private:
	char* _name;
	int _age;
};

person.cpp:

cpp 复制代码
#include "person.h"

//在类外定义成员时,需要使用::指明成员属于哪个类域。
void Person::showInfo(){
    cout << _name << "-" << _age << endl;
}

class与结构体struct的区别

C++需要兼容C语言,所以C++中struct可以定义结构体,也可以用来定义类。区别在于struct定义的类其成员默认访问权限是public,class定义的类成员默认访问权限是private。

this指针

this为一个const指针。

C++编译器给每个非静态的成员函数增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

cpp 复制代码
class A1 {
public:
	void f1() {
		cout << _a << endl;
	}
private:
	int _a;
};

<---------经过编译器处理后,相当于------->
class A1 {
public:
	void f1(A1 *const this) {
		cout << this->_a << endl;
	}
private:
	int _a;
};

this指针是非静态成员函数的第一个形参,当对象调用成员函数时,将对象的地址作为实参传入。在const成员函数中,this指向的是const类型的对象。

this指针存在哪?

this指针作为函数参数,在调用前将值给到ecx寄存器,然后push ecx进栈中。

运算符重载

函数原型:返回值类型 operator操作符(参数列表)

  1. 基本类型的运算符不能重载。
  2. .* ::``sizeof ?: . 不能重载。
cpp 复制代码
class Person
{
public:
	// 左操作数为this。
	bool operator==(const Person& p);
private:
	int _age;
};

bool Person::operator==(const Person& p) {
	return _age == p._age;
}

类的默认成员函数

构造函数

  1. 构造函数是一个特殊的成员函数,名字与类名相同,无返回值,创建类型对象时由编译器自动调用。
cpp 复制代码
#include <iostream>
using namespace std;

class A1 {
public:
	A1();
	A1(int a);
private:
	int _a;
};
//无参
A1::A1(){
	_a = 0;
}
//带参
A1::A1(int a) {
	_a = a;
}

int main()
{
	A1 a1;  //使用无参构造函数初始化
	A1 aa1(10);//使用带参构造函数初始化
	return 0;
}
  1. 构造函数的任务并不是开空间创建对象,而是初始化对象。
  2. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数(并不准确,请见C++对象模型),一旦用户显式定义编译器将不再生成。
  3. C++编译器生成的默认构造函数,初始化基本类型仍然是随机值,会调用自定义类型的默认构造函数进行初始化。
  4. 无参构造函数,全缺省构造函数,编译器生成的默认构造函数都认为是默认构造函数,三者会冲突。
初始化列表

以下三种情况必须使用初始化列表进行初始化。

  • const 成员
  • 引用成员
  • 没有默认构造函数的自定义类型成员

原因:在构造函数中用户编写的语句调用前,所有成员变量已定义完成。const和引用类型在定义时就必须初始化完成,不能在构造函数中二次赋值;自定义成员则在定义时需要调用其默认构造函数,没有则只能使用初始化列表调用其别的构造函数来定义。

cpp 复制代码
class C {
	int val;
public:
	C(int x) : val(x) {}
};

class A {
public:
	A(int x,int& ref,int val):_x(x),_ref(ref),_c(val){}
private:
	const int _x;
	int& _ref;
	C _c;
};

注意:初始化顺序为成员在类中声明的顺序,与初始化列表顺序无关。

以下代码为例:

cpp 复制代码
class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{
	}
	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2;
	int _a1;
};
int main() {
	A aa(1);
	aa.Print();
}

// 输出:1 随机值

析构函数

  1. 无参数无返回值,不可重载。
  2. 对象生命周期结束时,系统自动调用析构函数。
  3. 若未显式定义,自动生成默认的析构函数(并不准确,请见C++对象模型)。编译器生成的默认析构函数会调用自定义类型成员的析构函数。
  4. 若类中有动态申请的资源,必须要写析构函数!避免造成资源泄露。
  5. 局部对象的释放,先定义的先入栈也就后释放。

以下代码输出:

cpp 复制代码
class Person
{
public:
	Person(const char* name);
	~Person();
private:
	char* _name;
};

Person::Person(const char* name) {
	_name = new char[strlen(name) + 1];
	strcpy(_name, name);
}

Person::~Person() {
	cout << "free " << _name << endl;
	delete[] _name;
}

int main()
{
	Person p1("p1");
	Person p2("p2");
	return 0;
}

拷贝构造函数

  1. 其是构造函数的一个重载形式,只有一个形参,即本类类型对象的引用(一般使用const修饰)。
  2. 用已存在的类类型对象创建新对象时(如函数传参,拷贝临时变量)由编译器自动调用。
  3. 类中涉及到资源申请时,必须写拷贝构造函数!否则系统默认生成的为浅拷贝。
cpp 复制代码
class Person
{
public:
	Person(const char* name);
	~Person();
	Person(const Person& p);
private:
	char* _name;
};

Person::Person(const char* name) {
	_name = new char[strlen(name) + 1];
	strcpy(_name, name);
}

Person::Person(const Person& p) {
	_name = new char[strlen(p._name) + 1];
    strcpy(_name, p._name);
}

Person::~Person() {
	cout << "free " << _name << endl;
	delete[] _name;
}

C++11引入构造函数委托,可通过调用本类中的另一个构造函数来初始化当前对象。

cpp 复制代码
// 构造函数委托给 Person(const char*)
Person::Person(const Person& p):Person(p._name) {}

赋值运算符重载

  1. 参数const T&,返回值T&,检测是否是同一地址,返回*this
  2. 赋值运算符不能重载成全局函数,只能是成员函数。
  3. 没有显示实现,则编译器自动生成,进行浅拷贝。
cpp 复制代码
class Person
{
public:
	Person& operator=(const Person& p);
private:
	char* _name;
};

Person& Person::operator=(const Person& p) {
	if (this != &p) {
		// 1. 释放旧内存
		delete[] _name;
		// 2. 分配新内存
		_name = new char[strlen(p._name) + 1];
		// 3. 拷贝数据
		strcpy(_name, p._name);
	}
	return *this;
}
cpp 复制代码
int main(){
    Person p1=p2; //调用拷贝构造函数
    p1=p3;        //调用赋值重载
}

静态(static)成员

  1. 静态成员为类对象所共享,不为某个对象独有,存在全局静态区。
  2. 静态成员变量必须在类外定义,定义时不需要添加static关键字,其受访问控制符的影响。
  3. 静态成员可直接使用类::静态成员的方式访问。
  4. 静态函数没有this指针,无法访问任何非静态成员。

友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。

友元函数

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

cpp 复制代码
class Person
{
// 在该类中对该函数进行友元声明,则在该函数定义中可以直接访问p的私有变量
	friend ostream& operator<< (ostream& _cout, const Person& p);
public:
	explicit Person(int age);
private:
	int _age;
};

Person::Person(int age):_age(age){}
ostream& operator<<(ostream& _cout, const Person& p)
{
	_cout << "Person.age:" << p._age;
	return _cout;
}

int main() {
	Person p10(10);
	cout << p10 << endl;
}

友元类

在类A中声明另一个类B为其友元类,则B中可以直接访问A类型对象的私有成员。

cpp 复制代码
class Birthday
{
//声明Person类为该类的友元类
	friend class Person;
public:
	Birthday(int year = 2020) {}
private:
	int _year;
};

class Person
{
public:
	explicit Person(int age=18) :_age(age) {}
	void ShowBirthday() {
        // 在Person类中可直接访问Birthday的私有成员
		cout << _bir._year<<endl;
	}
private:
	int _age;
	Birthday _bir;
};
  • 友元关系为单向的,B是A的友元,但A不是B的友元。
  • 友元关系不具有传递性,B是A的友元,A是C的友元,但B不是C的友元。
  • 友元关系不能继承。

内部类

如果一个类定义在另一个类的内部,就叫做内部类。内部类是独立的类,外部类没有任何优越的的访问权限。sizeof(外部类)也不会计算内部类。

内部类是外部类的友元类,但外部类不是内部类的友元类。

相关推荐
指尖在键盘上舞动2 小时前
Cannot find matching video player interface for ‘ffpyplayer‘.解决方案
linux·ubuntu·ffmpeg·psychopy·ffpyplayer
为搬砖记录2 小时前
杰理AC695N soundbox 3.1.2打开ble宏的编译bug
c语言·开发语言·单片机·bug
桌面运维家2 小时前
Linux/Windows终端密码设置:保护你的vDisk数据
linux·运维·服务器
yeshihouhou2 小时前
# sse实现进度条功能
java
ErizJ2 小时前
面试 | 操作系统
linux·面试·职场和发展·操作系统·os
程序媛徐师姐2 小时前
Java基于微信小程序的线上教育商城,附源码+文档说明
java·微信小程序·线上教育商城小程序·java线上教育商城小程序·线上教育商城微信小程序·线上教育小程序·线上教育微信小程序
有梦想的小何2 小时前
从结算需求出发:基于库存日快照与分区的结算报表的Java实践
java·数据库·mysql
17(无规则自律)2 小时前
Leetcode第二题:用 C++ 解决字母异位词分组
c++·leetcode·哈希算法
韩立学长2 小时前
基于Springboot的商品库存管理系统369jr3t9(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
java·数据库·spring boot·后端