❀类和对象
# 1. 访问限定符
ob:三大特性:分装、继承、多态
public\protected\private
公有、保护和私有:
保护和私有的区别在继承体现
struct 默认是公有、class默认私有
#include <iostream>
using namespace std;
class A
{
public:
	void Print(int x) //成员函数存储在公共代码段 **在类里面实现的函数默认是内联函数**
	{
		_a = x;
		printf("a = %d\n", _a);
		cout << this << endl;
	}
private:
	int _a;
};
int main()
{
	A a;
	a.Print(10);
	printf("%p\n", &a);
	return 0;
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
类的大小:
class A{
private:
	int _a;
	int *p;
};
 2
3
4
5
对象中不存成员函数,成员函数存储在公共的代码区。若类没有成员变量,则sizeof(对象) = 1——不存储有效数据,只是为了占位表示对象存在
sizeof(A) = 8
声明和定义
1、声明和定义都放在类中(函数在类中定义),编译器可能会将其当成内联函数处理
变量可以指定类域访问成员变量:A::_a
this : 不允许在实参和形参中显式传递;但是允许在函数中调用,this->(成员变量),一般不会显示写出。this 指向的空间地址不能被改变,即A *const this,故在成员函数中,this = nullptr 是错误的
this 一般在栈上存储,有的编译器放到寄存器中;空指针不解引用就不会报错
# 2. 默认成员函数-6
# 构造函数——完成对象的初始化
函数名和类名相同,类实例化时自动调用初始化,
不是开空间。无返回值;可以重载。- 默认构造函数(不用传参数的实例对象):1、无参构造函数;2、全缺省构造;3、没写编译器默认产生的 (半缺省和全缺省的不是)
 
#include <iostream>
using namespace std;
class A
{
public:
	void Print() //成员函数存储在公共代码段
	{
		printf("a = %d\n", _a);
		cout << "this:" << this << endl;
	}
	A(int a = 1) //建议使用(全缺省 = 默认参数),实例化对象时可以不同单独给值;否则实例化对象时必须给参数
		:_a(6)  // 自己总结的:构造函数对参数的初始化优先级  函数中缺省值(1) >  初始化列表(6) > 声明缺省值(0)
	{
		_a = a;
		cout << "A Constructor a = " << _a << endl;
	}
	//A() //无参构造函数会和全缺省构成重载,但是调用存在歧义
	//{
	//	cout << "A Constructor Without Parameter" << endl;
	//}
private:
	int _a = 0;
};
class Person
{
public:
	void Print()
	{
		cout << "In Class Person:" << endl;
		cout << _name << " : " << _age << endl;
		_a.Print();
	}
private:
	char* _name[20];
	int _age;
	//A _a = A(2); //初始化列表没有定义,会优先调用这里的缺省值
	//A _a = 3;
	A _a; //声明没有给缺省值,则会使用构造函数中的缺省值。
};
int main()
{
	A a;
	a.Print();
	printf("&a = %p\n", &a);
	printf("\n");
	Person John;
	John.Print();
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
对于类中变量的初始化:若变量包含自定义类型,则变量会调用自定义类型的默认构造函数(不用参数调用的)初始化,若无 默认构造函数存在则会报错;对于内置类型,不处理。
不允许这样定义定义对象:A a();与函数定义右异议
# 析构函数
无参数无返回值
class Person
{
public:
	Person(size_t namesize = 20)
	{
		_name = (char*)malloc(sizeof(char) * namesize);
		if (_name == nullptr)
		{
			printf("malloc failed\n");
			exit(-1);
		}
		cout << "Constructor Initial Succeed" << endl;
	}
	void SendName(char* name = "ZhangSan")
	{
		int i = 0;
		while ((name[i]) != '\0')
		{
			_name[i] = name[i];
			i++;
		}
		_name[i] = '\0';
	}
	void Print()
	{
		cout << _name << endl;
	}
	~Person()
	{
		cout << _name << endl;
		free(_name);
		_name = nullptr;
		cout << "Destructor Free Space Succeed" << endl;
	}
private:
	char* _name;
	int _age;
};
int main()
{
	Person John;
	John.SendName("John");
	Person Marry;
	Marry.SendName("Marry");
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
可以看到实例化对象后,对于生命周期相同的对象,后创建的对象会被先销毁,因为:对象的函数调用会建立栈帧,构造和析构函数符合后进先出。
另外局部变量会被先销毁,static和全局变量后销毁
# 拷贝构造函数
默认的拷贝构造函数:内置成员,完成按照字节序拷贝(值’浅‘拷贝);自定义类型,调用它的拷贝构造。
class B
{
public:
	B(int a = 1, int b = 1, int c = 1)
	{
		_a = a;
		_b = b;
		_c = c;
	}
	B(B& t) //拷贝构造函数 与构造函数构成重载 ——只有单个形参,一般用const修饰
			//必须使用引用传参,否则会无穷递归
	{
		_a = t._a * 1;
		_b = t._b * 2;
		_c = t._c * 3;
	}
	void Print()
	{
		cout << _a << "-" << _b << "-" << _c << endl;
	}
private:
	int _a;
	int _b;
	int _c;
};
int main()
{
	B b1(1,2,3);
	B b2(b1);  //
	b2.Print();
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 赋值运算符也是默认成员函数(operator =)
1、内置类型的成员完成字节序值拷贝——浅拷贝
2、自定义类型,调用其operator =
class Date{    //默认生成的赋值浅拷贝可以用
private:
	int _month;
	int _day;
};
class Stack{   //需要深拷贝,默认的无法满足,否则析构两次
private:
	int *_a;
	int _size;
}
class MyQueue{  //默认生成的可用,因为他会调用自定义成员的赋值和析构
	Stack _pushSt; 
	Stack _popSt;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意:
赋值重载针对两个存在的对象,若对于不存在对象的赋值(初始化将要创建的对象),则调用拷贝构造
A a1;
A a2;
a2 = a1;   //赋值重载
A a3 = a1; //拷贝构造
 2
3
4
5
- 另外两个:没太大意义
 - 取地址和const取地址运算符重载
 
# 3. 运算符重载
operator不能重载的运算符:
.*  
::
sizeof
? :
.
 2
3
4
5
优先在类中成员寻找,若无,再到全局寻找重载函数。(VS中)
对于内置类型(int 等)数据,默认支持运算符,但是对于自定义类型,默认不支持,但是可以通过运算符重载使得支持。
class A
{
public:
	A(int a1 = 1, int a2 = 1, int a3 = 1)
	{
		_a1 = a1;
		_a2 = a2;
		_a3 = a3;
	}
	bool operator==(A a)
	{
		return _a1 == a._a1 && _a2 == a._a2 && _a3 == a._a3;
	}
private:
	int _a1;
	int _a2;
	int _a3;
};
int main()
{
	A a;
	A b;
	A c(3, 3, 3);
	cout << (a == b) << endl; // a.operator==(b)
	cout <<(a == c)<< endl;
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
bool operator>(const A &a){
    
}
A& operator=(const A &a){     //引用返回,出了作用域对象还在 
    if(this != &a)  // a1 = a1;排除给自己赋值的情况
   		 //..    			//如a1 = a2 = a3, a2 = a3返回的是a2的*this(对象别名),a1 = a2中而a是a2的别名。
    
    return *this;
}
A operator++(); //++a  a.operator++(&a)
A operator++(int);//后置++,与前置++构成函数重载 a.operator++(&a,0)
 2
3
4
5
6
7
8
9
10
11
12
13
14
- 输入输出流
 
class {
    void A::operator<<(ostream &out)   //const A* this , ostream &out 第一个参数是左操作数,第二个是右操作数
    {
        out << _a << endl;
    }
}
A a;
a.operator<<(cout); // 调用
//写成全局可以正常调用——但访问不了私有成员,——友元函数
ostream& operator<<(ostream &out, const A& a)   
{
    out << _a << endl;
    return out;
}
istream& operator>>(istream &in, A& a)   
{
    in >> a._a;
    return in;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 4. const修饰成员函数
bool operator==(const A a) const // 成员函数后+const保证了this指向的内容不被修改
{
    return _a1 == a._a1 && _a2 == a._a2 && _a3 == a._a3;
}
 2
3
4
类成员函数默认含有的 是A * cosnt this,指的是this的值不能被修改,它代表的是传入对象的地址。在外加了const变成了const A* const this。
关于权限放大和缩小的问题只针对const在*前的情况。
# 5. 友元
为了能让外部特定的函数访问类中的变量,可以在类中声明这个外部函数——友元函数(可以定义在类中任意位置)
友元不具有传递性、交换性。另外友元是破坏封装的行为,少使用。
class A
{
	friend void ExternFuncion(int a); //友元函数
	friend class B; //友元类
private:
	int _a1;
	int _a2;
	int _a3;
	B b;
};
 2
3
4
5
6
7
8
9
10
- 友元函数可以访问成员变量,但不能访问成员函数
 - 不能用const修饰,(const修饰*this)
 - 可以定义在类中任何地方,不受访问修饰符限制
 - 一个函数可以是多个类的友元函数
 
友元类
- 关系是单项的,B类中可以直接访问A类中的成员变量,而A不能访问B
 - 关系不能传递。
 
# 6. 初始化列表
class A
{
public:
	A(int a)
	{
		_a1 = a;
	}
private:
	int _a1;
};
class B
{
public:
	B(int a = 1, int b = 1, int c = 1)
		:_a(a)	 //变量的定义---相当于在定义的时候初始化 int x = 1;
		,_b(b)
		,_c(c) //1、const 类型 、引用类型只能通过初始化列表初始化
		,_aa(1) //2、自定义类型,且无默认构造函数的必须在初始化列表初始化  ;  其他类型可以在函数体内初始化
	{
		_a = a;// 允许函数体内和初始化列表共同初始化一个变量,但是初始化列表中一个变量只能出现一次。
		_b = b;      //相当于先定义后初始化,int x; x = 1;
		/*_c = c;*/
	}
private:
	int _a;  //变量的声明
	int _b;
	const int _c;
	A _aa;
};
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
对于自定义类型,虽然在初始化列表没有写,但是仍会在初始化列表中调用其默认构造函数;
建议:尽量使用初始化列表初始化,特别是自定义类型(因为若自定义类型在函数体内初始化,则会调用一次默认构造和一次赋值运算符重载;在初始化列表只会调用一次拷贝构造);应保证声明变量顺序和初始化顺序相同。
注意:成员变量在初始化列表中初始化的顺序依据声明的顺序,如上,无论初始化列表顺序怎么变,则初始化始终按照_a\ -b \ _c\ _aa顺序初始化。
# 7. 其他
# 初始化自定义类变量——隐式类型转换
无关类型——强制转换
相似类型——隐式转换
double d = 1.1;
int &i = d; //错误 应为const int &i = d; 隐式类型转换先赋值给临时变量
 2
class A
{
public:
	//explicit A(int a) //explicity 关键字可以保证A类型变量不能被隐士转换
	A(int a)
	{
		_a1 = a;
		cout << "Construction" << endl;
	}
	A(const A& a)
	{
		_a1 = a._a1;
		cout << "Copy Construction" << endl;
	}
	~A()
	{
		cout << "Destructor :" << _a1 << endl;
	}
private:
	int _a1;
};
int main()
{
	A a1(1);
	A a2 = 2; // int -> A类型;隐式转换 先构造临时对象 A(2),再用这个对象拷贝构造a2,编译器优化后变成一个构造
	A(3);   //匿名对象的生命周期只在本行
	A a4 = 4; 
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 静态成员变量
class A
{
public:
	static int Get_a1() //静态成员函数,没有this指针,且不能访问非静态成员,只能访问静态成员变量和函数
	{
		return _a1;
	}
private:
	static int _a1; //_a1存放在静态区,不计入类的大小,属于整个类(可用于计算该类定义了多少对象)
};
int A::_a1 = 0; //静态成员变量的初始化,属于该类但不属于某个对象
int main()
{
	A a1;
	cout << sizeof(a1) << endl;
	cout << sizeof(A) << endl;
    a1.Get_a1; //对静态成员函数,使用对象或指定类域 -突破类域都可以调用。
    A::Get_a1
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 类变量缺省值
class B
{
public:
	B()
	{
		cout << "B Constructor" << endl;
	}
private:
	int _b;
};
class A
{
public:
	A()
		: _a2(1)
	{
		cout << "A Constructor" << endl;
		cout << "_a2 = " << _a2 << endl;
		_p[0] = 'h';
		_p[1] = 'a';
		_p[8] = '\0';
		cout << "_p = " << _p << endl;
	}
private:
	static int _a1; //静态成员*不能*给缺省值
	int _a2 = 0; //声明给缺省值,这里仍然是对对象的声明。初始化列表若没有自己写对成员变量初始化,则使用缺省值初始化
	int* _p = (int*)malloc(10*sizeof(int)); //现在不会开辟新的空间,
								//但是调用构造函数若初始化列表中没有,则会调用这里的语句
	B b1 = 10;
};
int A::_a1 = 0;
int main()
{
	A a1;
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 内部类
类中定义一个类
内部类和在全局定义类基本一样,只受到外部类的限制
内部类天生是外部类的友元。(单向的)
# c++动态内存分配
class A
{
public:
	A(int a = 0)
		: _a2(a)
	{
		cout << "A Constructor" << endl;
	}
private:
	//static int _a1; 
	int _a2 = 0; 
};
int main()
{
	//int* p1 = (int*)malloc(0xffffffff);//malloc开辟出4GB的空间——64位,不会初始化
	//cout << sizeof(p1) << endl;
	//free(p1);
	int *p1 = (int*)operator new(sizeof(int)* 4); //库函数operator new
	operator delete(p1);
		
	A* pa = new A[10]; // new 开空间会调用类的构造函数进行初始化 C++11支持初始化: A[10] = {1,2,3},其他默认为0
	cout << sizeof(*pa) << endl;
	delete[] pa; // delete会调用类的析构函数并释放空间
	int* pnit1 = new int[10]{1,2,3}; //对于内置类型的指针,定义及初始化
	int* pnit2 = new int(1); //内置类型的指针单个空间初始化
	cout << " pnit1[3]:" << pnit1[3] << endl;
	cout << "*pnit2:" << *pnit2 << endl;
	delete[] pnit1;  
	delete pnit2;
	return 0;
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- new(操作符)
 
实际上调用operate new函数 + 类对象构造函数进行空间的分配,而operate new的底层调用malloc和内存分配失败异常判断 。
A* pa = (A*)operate new(sizeof(A));
new(pa)A(2); //placement-new,调用类中构造函数初始化
 2
以上实现等价与new 注意:构造函数不能显式调用,显示必须使用定位new。
- delete
 
调用析构 + operate delete  ,operate delete 调用 free + 失败异常机制
paa->~A();
operator delete(paa);
 2
以上实现等价与new 注意:析构函数可以如上显式调用
operate delete和operate new可以在类中定义重载函数
void* operate new(size_t n)
{
	...
}
void operate delete(void* p)
{
	...
}
 2
3
4
5
6
7
8
- 自定义对象用malloc开辟空间并初始化 定位new
 
A* pa = (A*)malloc(sizeof(A));
new(pa)A(2); //placement-new,调用类中构造函数初始化malloc空间
 2
- 面向对象
 
面向对象出现错误:抛异常
面向过程出现错误:打印错误码errno
try
{
	char *p = new char[1024u*1024u*1024u*2u - 1];//创建失败不会异常终止
}
catch  (const exception& e) //try 正常执行,跳过
{
	cout << e.what() << endl;
}
 2
3
4
5
6
7
8
new/delete回调用构造和析构函数
new失败后抛异常。
replacement new
定位new——对已有的空间调用构造函数初始化
int mian()
{
	A*p = (A*)malloc(sizeof(A));
	new(p)A;
    new(p)A(1);
	A* p1 = new A(1);
	delete p1;
	
	// 等价于
	A* p1 = (A*)operator new(sizeof(A));
	new(p1)A(1);
	
	p1->~A(); //析构函数可以显式调用,构造不行
	operator delete(p1);
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 内存泄漏
 
主动防范:智能指针
# 编译器的优化
只有构造和拷贝构造才会被优化,在一个表达式的连续步骤中,临时对象和匿名对象(通常在传值参数或函数返回值)会被优化掉。
匿名对象的生命周期只在这一行,结束后会调用析构函数。
# 权限的放大与缩小
#include <iostream>
using namespace std;
class A{
public:
        A(int a = 1)  
            :_a(a)
        {   
            cout << "constructor" << endl;
                            
        }   
        void Print() const           //A* const this —— 原生的this形参的表示,this的内容不能被更改
        {   						//const A* const this —— 加了const后的,*this(对象)不会被改变,即this指向的内																									容不会被改
            cout << "hello world" << endl;                  
        }   
private:
        int _a;         
};
int main()
{
     A a1; 
     a1.Print();  // &a1 -> A*
     const A a2; 
     a2.Print(); //掉用不动,&a2 -> const A*
     return 0;                 
}
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 其他
- 类的定义存在代码段。成员函数在代码段。对象中不存放普通函数的地址,只存放成员变量。
 
