cpp笔记(二)

记录一下c++的学习过程,笔记如有错误,欢迎指出!!!

文章目录

  • 对象的构造和析构
    • 构造函数
      • 特性
      • 构造函数的调用
      • 构造函数的调用规则
      • 拷贝构造函数的调用时机
      • 深拷贝与浅拷贝
      • 初始化列表
      • 类对象做类中成员
      • explicit关键字
    • 析构函数
      • 特性
  • new和delete
    • malloc的缺陷
    • new和malloc的区别
    • new和delete的使用
  • 静态成员变量(函数)
    • 静态成员变量注意事项
    • 静态成员函数注意事项
  • 单例模式
    • ==单例模式案例-打印机==
      • 面试题:在main函数执行之前执行函数
  • C++对象模型初探
    • 类对象的大小
    • this指针
      • 用途

对象的构造和析构

对象的构造函数,按照代码执行顺序执行,析构函数按照出栈顺序执行(与构造函数顺序相反)

构造函数

特性

  1. 没有返回值,不用写void
  2. 函数名与类名相同
  3. 可以有参数,可以发生重载
  4. 构造函数,由编译器自动调用,无须手动调用

按参数分类

  1. 无参
  2. 有参

按类型分类

  1. 普通构造函数
  2. 拷贝构造函数

构造函数的调用

  1. 括号法
  2. 显式法
  3. 隐式法
class Person
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}

	Person(int age)
	{
		m_Age = age;
		cout << "Person的有参构造函数调用" << endl;
	}

	//拷贝构造函数, 注意形参 const Person &p, &:为了防止拷贝构造函数进入递归循环, const为了防止p被修改
	//当实参传入形参时,就要执行拷贝构造函数,即使函数体没内容
	Person(const Person &p)  
	{
		cout << "Person的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}

	//析构函数
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}

	int m_Age;
	
};

//构造函数的调用
void test01()
{
	Person p;

	//1、括号法
	Person p1(10);
	Person p2(p);

	//注意事项一 
	//不要用括号法 调用无参构造函数  Person p3();  编译器认为代码是函数的声明
	
	//2、显示法
	Person p3 = Person(10); //有参构造
	Person p4 = Person(p3); //拷贝构造

	Person(10); //匿名对象  特点: 当前行执行完后 立即释放

	cout << "aaa" << endl;

	//注意事项二
	//不要用拷贝构造函数 初始化 匿名对象 Person(p3); 编译器认为 Person p3对象实例化  如果已经有p3  p3就重定义

	//3、隐式法  
	Person p5 = 10; //Person p5 = Person(10);
	Person p6 = p5;

}

构造函数的调用规则

  1. 默认情况下,编译器会给一个类至少添加三个函数, 默认构造函数(空实现) 析构函数(空实现) 拷贝构造(值拷贝)
  2. 如果我们自己提供了有参构造函数,编译器就不会提供默认构造函数,但是依然会提供拷贝构造函数
  3. 如果我们自己提供了 拷贝构造函数,编译器就不会提供其他构造函数

拷贝构造函数的调用时机

  1. 用已经创建好的对象来初始化新的对象 调用拷贝构造函数
  2. 值传递的方式 给函数参数传值 此时函数参数不是引用
  3. 以值方式 返回局部对象,返回值相当于一个匿名对象,给接收者进行赋值,函数结束后,临时对象会被
class Person
{
public:
	Person()
	{
		cout << "Person的默认构造函数调用" << endl;
	}

	Person(int age)
	{
		m_Age = age;
		cout << "Person的有参构造函数调用" << endl;
	}
	//拷贝构造函数
	Person(const Person &p)
	{
		cout << "Person的拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}
	//析构函数
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
	int m_Age;
};


//1、用已经创建好的对象来初始化新的对象
void test01()
{
	Person p1(18);

	Person p2 = Person(p1);

	cout << "p2的年龄:" << p2.m_Age<< endl;

}

//2、值传递的方式 给函数参数传值
void doWork(Person p)
{

}
void test02()
{
	Person p1(100);
	doWork(p1);

}

//3、以值方式 返回局部对象
Person doWork2()
{
	Person p;
	return p;
}

void test03()
{
	Person p = doWork2();
}

深拷贝与浅拷贝

浅拷贝只拷贝变量中存放的内容,例如只拷贝地址的值,而不拷贝地址指向的内容,当有多个对象指向同一块内容时,释放对象时,就会多次释放相同地址指向的内容,导致报错。 因此,需要深拷贝解决,深拷贝不是简单的值的拷贝,而是另外开辟一块空间,拷贝地址指向的内容

class Person
{
public:

	Person( char * name ,int age )
	{
		m_Name = (char *) malloc(strlen(name) + 1);
		strcpy(m_Name, name);
		m_Age = age;
	}

	Person(const Person&p)
	{
	   m_Name =(char*)malloc(strlen(p.m_Name) + 1);
	   strcpy(m_Name, p.m_Name);
	   m_Age = p.m_Age;
	}

	~Person()
	{
		if (m_Name != NULL)
		{
			cout << "Person析构调用" << endl;
			free(m_Name);
			m_Name = NULL;
		}
	}

	char * m_Name; //姓名
	int m_Age;     //年龄
};


void test01()
{
	Person p("德玛西亚", 18);
	cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;

	Person p2(p);
	cout << "姓名: " << p2.m_Name << " 年龄: " << p2.m_Age << endl;
}

初始化列表

可以用初始化列表语法,给类中的属性进行初始化,形式如下

//构造函数名称后  : 属性(值), 属性(值)...
Person(int a, int b, int c) : m_A(a), m_B(b), m_C(c)
{
}

类对象做类中成员


class Phone
{
public:

	Phone(string name) :m_Pname(name)
	{
		cout << "Phone的有参构造函数调用" << endl;
	}
	Phone(const Phone& phone)
	{
		m_Pname = phone.m_Pname;
		cout << "Phone的拷贝构造函数调用" << endl;

	}
	~Phone()
	{
		cout << "Phone的析构函数调用" << endl;
	}
	string m_Pname;
};

class Game
{
public:
	Game(string name): m_Gname(name)
	{
		cout << "Game的有参构造函数调用" << endl;
	}
	Game(const Game& game)
	{
		m_Gname = game.m_Gname;
		cout << "Game的拷贝构造函数调用" << endl;
	}
	~Game()
	{
		cout << "Game的析构函数调用" << endl;
	}
	string m_Gname;

};


class Person
{
public:
	Person(string p_name, string g_name, string name) :m_phone(p_name), m_game(g_name), m_name(name)
	{
		cout << "Person的有参构造函数调用" << endl;
	}
	Person(Phone phone, Game game, string name) :m_phone(phone), m_game(game), m_name(name)
	{
		cout << "Person的有参构造函数调用" << endl;
	}
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}
	string m_name;
	Phone m_phone;
	Game m_game;

};


void test01()
{
	Phone phone("vivo");
	Game game("王者荣耀");
	Person person(phone, game,  "jiege");   //先将实参传入形参,依据调用惯例,从右向左入栈,依次调用拷贝构造函数
										//随后,在函数中,再次依据参数列表调用拷贝构造函数
	//Person person("vivo", "王者荣耀", "jiege");

explicit关键字

​ explicit用途: 防止利用隐式类型转换方式来构造对象

class MyString
{
public:
	MyString(char * str)
	{
	}
	//explicit用途: 防止利用隐式类型转换方式来构造对象
	explicit MyString(int len)
	{	
	}
};
void test01()
{
	MyString str1(10);
	MyString str2 = MyString(100);
	//MyString str3 = 10; // "10"

}

析构函数

如果有属性分配在堆区,此时需要自己写析构函数手动释放

特性

  1. 没有返回值,不用写void
  2. 函数名与类名相同,函数名前加~
  3. 不可以由参数,不可以发生重载
  4. 析构函数,也是由编译器自动调用一次,无须手动调用

new和delete

malloc的缺陷

  1. 程序员必须确定对象的长度。
  2. malloc返回一个void指针,c++不允许将void赋值给其他任何指针,必须强转。
  3. malloc可能申请内存失败,所以必须判断返回值来确保内存分配成功。
  4. 用户在使用对象之前必须记住对他初始化,构造函数不能显示调用初始化(构造函数是由编译器调用),用户有可能忘记调用初始化函数。

new和malloc的区别

  1. malloc 和 free 属于 库函数 new 和delete属于 运算符
  2. malloc不会调用构造函数 new会调用构造函数
  3. malloc返回void* C++下要强转 new 返回创建的对象的指针

new和delete的使用

  1. 堆区开辟数组,一定会调用默认构造函数
  2. 释放数组时候 需要加[]
  3. 栈上开辟数组,可以没有默认构造
  4. 不建议用void *接收new开辟的空间,因为无法用delete释放,非要这样的话,需要用强制转换
  5. new可以在构造函数为私有的情况下,创建对象,返回对象指针
class Person
{
public:
	Person()
	{
		cout << "Person构造函数调用" << endl;
	}
	Person(int a)
	{
		cout << "Person有参构造调用" << endl;
	}
	~Person()
	{
		cout << "Person析构函数调用" << endl;
	}
};
void test01()
{
	Person * p = new Person;  //调用默认构造函数,new Person后也可以调用 有参构造函数,或者拷贝构造函数
	delete p;                 //调用析构函数
}

//利用new开辟数组
void test03()
{
	int * pInt = new int[10];
	double * pD = new double[10];

	//堆区开辟数组,一定会调用默认构造函数
	Person * pPerson = new Person[10]; //调用10次默认构造函数

	//释放数组时候  需要加[]
	delete [] pPerson;  //  调用10次析构函数
	Person pArray[10] = { Person(10), Person(20), Person(20) };   //调用三个有参构造函数,7个默认构造函数

}

静态成员变量(函数)

静态成员变量注意事项

  1. 静态成员变量 :编译阶段就分配了内存,即采用单例模式时,在静态变量定义,静态变量时就调用默认构造函数,此时构造函数比main函数早执行
  2. 类内声明 、类外初始化,不通过构造函数初始化
  3. 静态成员变量 所有对象都共享同一份数据
  4. 可以通过对象或类名访问,私有静态成员变量外部访问不到

静态成员函数注意事项

  1. 所有对象都共享同一个func函数
  2. 静态成员函数 不能访问非静态成员变量
  3. 静态成员函数 能访问静态成员变量
  4. 可以通过对象或类名访问,私有静态成员函数外部访问不到
class Person
{
public:
	static int m_A;

	static void func()
	{
		//m_C = 100; //静态成员函数 不能访问非静态成员变量
		m_A = 100; //静态成员函数 能访问静态成员变量
		cout << "func调用" << endl;
	}

	int m_C;

private:
	static int m_B; //私有静态成员变量 

	static void func2()
	{
	
	}
};

int Person::m_A = 0;
int Person::m_B = 0;


void test01()
{
	//1、通过对象进行访问
	Person p1;
	cout << p1.m_A << endl;

	Person p2;
	p2.m_A = 100;

	cout << p1.m_A << endl;

	//2、通过类名进行访问
	cout << Person::m_A << endl;

	//静态成员变量 也是有访问权限的,私有权限类外访问不到
	//cout << Person::m_B << endl;

}

void test02()
{
	//通过对象
	Person p1;
	p1.func();
	//通过类名
	Person::func();

	//Person::func2();  静态成员函数也是有访问权限的

}

单例模式

一个类只能实例化一个对象

class ChairMan
{
public:
    static ChairMan * getInstacne()
	{
		return singleMan;
	}
private:
	//将构造函数私有化,不可以创建多个对象
	ChairMan(){};
	ChairMan(const ChairMan &){};

private:
	//将主席指针 私有化,对外提供只读接口
	static  ChairMan * singleMan; //类内声明  类外初始化
};

ChairMan *  ChairMan::singleMan = new ChairMan;
void test01()
{
     //需要屏蔽拷贝构造函数和默认构造函数
	/*ChairMan c1;
	ChairMan c2;
	ChairMan * c3 = new ChairMan;*/  
    //ChairMan * c3 = new ChairMan(*c1);

    //设为只读,不能修改
	ChairMan * c1 = ChairMan::getInstacne();
	ChairMan * c2 = ChairMan::getInstacne();

	if (c1 == c2)
	{
		cout << "c1 = c2" << endl;
	}
	else
	{
		cout << "c1 != c2" << endl;
	}

}

单例模式案例-打印机

面试题:在main函数执行之前执行函数

针对此案例:

  1. 由于静态成员的定义在编译时执行,所有new后面调用的此构造函数在main函数之前执行,只执行一次
  2. 只有一个实例,只进行一次构造函数,即使没有声名m_count为static, m_count也只初始化一次
class Printer
{
public:
	static Printer * getInstance()
	{
		return printer;
	}

	void printText(string text)
	{
		m_Count++;
		cout << text << endl;
	}

	int m_Count;

private:
	Printer()
	{ 
		m_Count = 0;
		cout << "打印机构造调用" << endl;   // 由于静态成员的定义在编译时执行,所有此构造函数在main函数之前执行,只执行一次
	};
	Printer(const Printer & p){};
	static Printer * printer;
};

Printer * Printer::printer = new Printer;

void test01()
{
	//只有一个实例,只进行一次构造函数,即使没有声名m_count为static, m_count也只初始化一次
	Printer * p1 = Printer::getInstance();
	p1->printText("入职证明");
	p1->printText("离职证明");
	p1->printText("加薪申请");
	p1->printText("旅游申请");

	cout << "打印机使用次数: " << p1->m_Count << endl;

	Printer * p2 = Printer::getInstance();
	p2->printText("调休申请");

	cout << "打印机使用次数: " << p1->m_Count << endl;

}

int main(){
	cout << "main函数调用" << endl;
	test01();
	system("pause");
	return EXIT_SUCCESS;
}

C++对象模型初探

类对象的大小

#pragma pack(show) //以警告的形式显示对齐模数
#pragma pack(n)       //设置对齐模数为n

只有非静态成员变量属于类对象上, 成员函数,静态成员函数,静态成员变量占用的内存都不在类对象上,即sizeof(类对象)的结果是类中非静态成员变量占用的字节数(经过内存对齐后的)

#pragma pack(show)
class Person
{
public:

	int m_A; //只有非静态成员变量  属于类对象上(即sizeof时占内存) 4个字节

	void func( )  //成员函数  并不属于类对象上(即sizeof时不占内存),方法与属性分开存储
	{
	}

	static int m_B; //静态成员变量  不属于类对象上(即sizeof时不占内存)

	static void func2()//静态成员函数  不属于类对象上(即sizeof时不占内存)
	{
		
	}

	double m_C; //只有非静态成员变量  属于类对象上(即sizeof时占内存) 8个字节

};
int Person::m_B = 0;

void test01()
{
	//空类的实例的sizeof结果是1  原因  每个对象都应该在内存上有独一无二的地址,因此给空对象分配1个字节空间
	Person p1;
	//  空对象 大小 1  
	cout << "sizeof = " << sizeof(p1) << endl;  //最终结果:一共12字节,但由于存在内存对齐,占16字节(假设对齐模数是8)

}

this指针

用途

  1. 解决名称冲突
  2. this指针 隐式加在每个成员函数中
  3. this指针 指向 被调用的成员函数 所属的对象
  4. 函数的返回值是引用时,返回的是经过函数处理后的原对象,返回值是类型时,返回的是经过函数处理后的对向的拷贝,与原对象不是一个东西,参考下面的链式编程
class Person
{
public:
	Person(int age)
	{
		//用途1 :解决名称冲突
		this->age = age;
	}

	//this指针 隐式加在每个成员函数中
	bool compareAge(Person &p)
	{
		if (this->age == p.age)
		{
			return true;
		}
		return false;
	}

	Person&  personAddPerson(Person &p)
	{
		this->age += p.age;
		return *this; //*this 就是本体
	}

	int age;
};

void test01()
{
	//this指针 指向 被调用的成员函数 所属的对象
	Person p1(10);

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


	Person p2(10);

	bool ret = p1.compareAge(p2);
	if (ret)
	{
		cout << "p1与p2年龄相等" << endl;
	}

	p1.personAddPerson(p2).personAddPerson(p2).personAddPerson(p2); //链式编程
	cout << "p1的年龄为: " << p1.age << endl;

}
#pragma pack(show)
class Person
{
public:

	int m_A; //只有非静态成员变量  属于类对象上(即sizeof时占内存) 4个字节

	void func( )  //成员函数  并不属于类对象上(即sizeof时不占内存),方法与属性分开存储
	{
	}

	static int m_B; //静态成员变量  不属于类对象上(即sizeof时不占内存)

	static void func2()//静态成员函数  不属于类对象上(即sizeof时不占内存)
	{
		
	}

	double m_C; //只有非静态成员变量  属于类对象上(即sizeof时占内存) 8个字节

};
int Person::m_B = 0;

void test01()
{
	//空类的实例的sizeof结果是1  原因  每个对象都应该在内存上有独一无二的地址,因此给空对象分配1个字节空间
	Person p1;
	//  空对象 大小 1  
	cout << "sizeof = " << sizeof(p1) << endl;  //最终结果:一共12字节,但由于存在内存对齐,占16字节(假设对齐模数是8)

}
  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值