类&对象

面向对象的三大特性

封装,继承,多态

封装性

将数据和方法封装在一起

类的访问限定符分为公有的:public,私有的:private,protected。若类中无访问限定符,则默认为私有的

类的作用域

对象通过 . 直接访问公有成员,指向对象的指针通过 -> 也可以直接访问对象的公有成员

#include <iostream>
using namespace std;

class Person
{
public:
	void Display()
	{
		cout << _name << "-" << _sex << "-" << _age << endl;
	}
public:
	char* _name;   
	char* _sex;   
	int _age;
};

void Test()
{
	Person p;
	p._name = "lisa";
	p._age = 15;
	p._sex = "女";
	p.Display();

	Person* ptr = &p;
	ptr->_name = "peter";
	ptr->_age = 18;
	ptr->_sex = "男";
	ptr->Display();
}

int main()
{
	Test();
	system("pause");
	return 0;
}

在类外定义成员,需使用 :: 作用域解析符指明成员属于哪个类域

class Person
{
public:
	void Display();
public:
	char* _name;
	char* _sex;
	int _age;
};
void Person::Display()
{
	cout << _name << "-" << _sex << "-" << _age << endl;
}

类对象存储

每个对象的大小为类中所有成员变量的大小之和,内存对齐

成员函数属于公共函数,不算入类的大小,不同对象均调用同一个函数,节省空间


空类

没有成语变量的类

#include <iostream>
using namespace std;

class A
{

};

class B
{
	void func()
	{

	}
};

int main()
{
	cout << sizeof(A) << endl;//1
	cout << sizeof(B) << endl;//1
	system("pause");
	return 0;
}

为什么空类的大小为1?

若对类实例化,给A和B分别定义一个成员对象,A a和B b,则为了区分成员对象分别开辟一个字节空间标识a和b

隐含的this指针

1.每个成员函数都有一个指针形参,即为this指针(构造函数无this指针)

2.编译器对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的this指针

3.this指针是成员函数隐含指针形参,是编译器自己处理的,即我们不能在成员函数的形参中添加this指针,也不能在调用时显

示传递对象的地址给this指针


为什么没有给成员函数传参,却可以打印出不同的数据?

隐式传参,this指针

类的默认成员函数

数据:private 函数:public

构造函数

成员变量为私有的,要对他们进行初始化,必须用一个公有函数来进行,同时这个函数有且仅在定义对象时自动执行一次

特征:

1.函数名与类名相同

2.无返回值

3.对象实例化时系统自动调用对应的构造函数

4.构造函数可以重载

5.构造函数可以在类内定义,也可以在类外定义

6.若类定义中没有给出构造函数,则C++编译器自动产生一个缺省的(无参)构造函数。若我们自己定义了一个构造函数,系统

不会自动生成缺省的构造函数

7.无参的构造函数和全缺省的构造函数都认为是缺省构造函数,不可同时存在于一个类中

#include <iostream>
using namespace std;

class Date
{
public:
	//无参的构造函数
	Date()
	{

	}
	//带参的构造函数
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void show()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.show();
	Date d2(1990, 1, 1);
	d2.show();//1990-1-1
	system("pause");
	return 0;
}
#include <iostream>
using namespace std;

class Date
{
public:
	//全缺省参数的构造函数
	Date(int year = 2018, int month = 3, int day = 19)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void show()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d;
	d.show();//2018-3-19
	system("pause");
	return 0;
}

初始化列表

以一个冒号开始,接着一个逗号分隔数据列表,每个数据成员都放在一个括号中进行初始化

无论是否采用初始化列表进行初始化,编译器一定会走一遍初始化列表这个过程

哪些成员变量必须放在初始化列表里?

1.常量成员变量(创建时必须初始化)   const int N;

2.引用类型成员变量(创建时必须初始化)   int& ref;

3.没有缺省构造函数的类成员变量

#include <iostream>
using namespace std;

class Time
{
public:
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	Date(int year, int month, int day, const Time& t)
	{
		_year = year;
		_month = month;
		_day = day;
		_t = t;
	}

private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	const int N;//常量成员变量
	int& ref;//引用类型成员变量
	Time _t;//没有缺省构造函数的类成员变量
	system("pause");
	return 0;
}

成员变量按照声明顺序依次初始化,而非初始化列表的顺序

#include <iostream>
using namespace std;

class A
{
public:
	A(int x)
		:_a2(x)
		,_a1(_a2)
	{
		cout << _a1 << endl;//随机值
		cout << _a2 << endl;//10
	}
private:
	int _a1;
	int _a2;
};

int main()
{
	A a(10);
	system("pause");
	return 0;
}

若成员变量为非内置类型,则会调用缺省构造函数进行初始化

若成员变量为内置类型,则什么都不做

拷贝构造函数

创建对象时使用同类对象来进行初始化,拷贝构造函数是特殊的构造函数

特征:

1.拷贝构造函数其实是一个构造函数的重载

2.拷贝构造函数的参数必须使用引用传参,使用传值的方式会引发无穷递归调用

3.若未显示定义,系统会默认缺省的拷贝构造函数,缺省的拷贝构造函数会依次拷贝类成员进行初始化

#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{}
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(d1);
	Date d3 = d1;
	system("pause");
	return 0;
}

在类里的对象可以访问私有成员

若在类中写了拷贝构造函数就必须写构造函数,系统不会自动生成默认构造函数

若成员变量为内置类型,则什么都不做

析构函数

当一个对象的生命周期结束时,C++编译系统会自动调用一个成员函数

特征:

1.析构函数在类名前加上字符~

2.析构函数无参数无返回值

3.一个类有且只有一个析构函数。若未显示定义,则系统会自动生成缺省的析构函数

4.对象生命周期结束时,C++编译系统自动调用析构函数

5.析构函数并不是删除对象,而是做一些清理工作 

在顺序表类中若不写拷贝构造和析构函数,系统自动生成的拷贝构造和析构函数无法实现

//基于动态数组的顺序表
#include <iostream>
using namespace std;

class SeqList
{
public:
	//构造函数
	SeqList(size_t capacity = 0)
	{
		if (capacity > 0)
		{
			_a = (int*)malloc(sizeof(int)*capacity);
			_size = 0;
			_capacity = capacity;
		}
		else
		{
			_a = NULL;
			_size = 0;
			_capacity = capacity;
		}
	}
	//析构函数
	~SeqList()
	{
		if (_a)
			free(_a);
		_a = NULL;
		_size = 0;
		_capacity = 0;
	}
private:
	int* _a;
	size_t _size;
	size_t _capacity;
};

int main()
{
	SeqList s1(10);
	SeqList s2(s1);
	system("pause");
	return 0;
}

若成员变量为内置类型,则什么都不做

运算符重载

特征:

1.函数名:operator+合法的运算符(重载<运算符的函数名:operator<)

2.重载运算符以后,不能改变运算符的优先级,结合性,操作数个数

5个C++不能重载的运算符:

.*   ::   sizeof   ?:   .

#include <iostream>
using namespace std;

class Date
{
public:
	void show(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date& d)
	{
		return this->_year == d._year
			&& this->_month == d._month
			&& this->_day == d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.show(1990, 1, 1);
	Date d2;
	d2.show(1990, 1, 1);
	cout << d1.operator==(d2) << endl;
	cout << (d1 == d2) << endl;
	system("pause");
	return 0;
}

 

若成员变量为内置类型,则什么都不做

赋值运算符重载

拷贝构造函数是创建对象,使用一个已有对象来初始化这个准备创建的对象

赋值运算符重载是对一个已存在的对象进行拷贝赋值

#include <iostream>
using namespace std;

class Date
{
public:
	//构造函数
	Date(int year = 2018, int month = 3, int day = 20)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	Date(const Date& d)
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{}
	//赋值运算符重载
	Date& operator=(const Date& d)
	{
		if (this != &d)//取地址
		{
			this->_year = d._year;
			this->_month = d._month;
			this->_day = d._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(d1);//拷贝构造
	Date d3;
	d1 = d3;//赋值运算符重载
	system("pause");
	return 0;
}
赋值表达式有返回值


若成员变量为内置类型,则什么都不做

const修饰成员函数


const对象可以调用const成员函数

非const对象可以调用const成员函数和非const成员函数

void show() const{}和void show(){}可以在一个类中同时存在,构成函数重载

内联函数

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

特征:

1.inline是一种以空间换时间的做法,省去调用函数的开销。代码很长或者含有循环,递归的函数不宜使用内联

2.inline对于编译器来说只是一个建议,编译器会自动优化。若定义为内联的函数体内含有循环,递归等,编译器优化时会忽

略内联

3.inline必须和函数定义放在一起,仅将inline放在函数声明前是不起作用的

4.定义在类内的成员函数默认定义为内联函数

*宏

优点:

增强代码可维护性(宏常量)

没有压栈,提高效率(宏函数)

缺点:

不方便调试(在预处理时调用,不用编译)

代码可读性较差,可维护性较差,容易出错(宏函数)

没有类型安全检查

#include <stdio.h>

#define Swap(a,b) do{int tmp = a; a = b; b = tmp;}while (0);//只执行一次

int main()
{
	int x1 = 10;
	int x2 = 100;
	Swap(x1, x2);
	int x3 = 5;
	int x4 = 0;
	Swap(x3, x4);
	system("pause");
	return 0;
}

若无{},则重定义了tmp

内联函数可以替换宏函数

const,枚举可以替换宏常量

友元函数

以friend声明的函数,允许在类外访问该类中的任何成员

特征:

1.友元函数不是类的成员函数

2.友元函数可以通过对象访问所有成员,包括私有和保护成员

输入输出运算符的重载的友元函数

#include <iostream>
using namespace std;

class Date
{
public:
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

	Date(int year = 1990, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "-" << d._month << "-" << d._day << endl;
	return out;
}

istream& operator>>(istream& in, Date& d)
{
	cout << "请输入年月日:" << endl;
	in >> d._year;
	in >> d._month;
	in >> d._day;
	return in;
}

int main()
{
	Date d(2018, 3, 31);
	cout << d;
	cin >> d;
	system("pause");
	return 0;
}


友元类

整个类可以是另一个类的友元(单向的)

友元破坏了封装性

类的静态成员


类里面用static修饰的成员

类的静态成员不属于某个对象,属于类的所有对象

静态的成员函数无this指针,可以使用::作用域访问符直接调用静态成员函数,但不可以访问普通成员函数

#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1990, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{
		++_count;
	}
	Date(const Date& d)
		:_year(d._year)
		, _month(d._month)
		, _day(d._day)
	{
		++_count;
	}
	void Show() const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		GetCount();
	}
	static int GetCount()
	{
		//_year = 10;
		//Show();
		return _count;
	}
private:
	int _year;
	int _month;
	int _day;

	static int _count;
};

int Date::_count = 0;

int main()
{
	Date d1(2018, 4, 2);
	Date d2(2018, 10, 30);

	//cout<<d1._count<<endl;
	//cout<<d2._count<<endl;
	//cout<<Date::_count<<endl;

	cout << d1.GetCount() << endl;//2
	cout << d2.GetCount() << endl;//2
	cout << Date::GetCount() << endl;//2

	system("pause");
	return 0;
}

构造拷贝赋值函数的N种调用情况

#include <iostream>
using namespace std;

class Date
{
public :
	Date()
	{
		cout<<"Date()" <<endl;
	}

	Date(const Date& d)
	{
		cout<<"Date(const Date& d)" <<endl;
	}

	Date& operator=(const Date& d )
	{
		cout<<"Date& operator=(const Date& d)"<< endl;
		return *this ;
	}

	~Date()
	{
		cout<<"~Date()" <<endl;
	}

	void Show()
	{}
};

void fun1 (Date d)
{}

Date fun2()
{
	Date ret;
	return ret;
}

Date fun3()
{
	return Date();
}

int main()
{
	//Date().Show();//一次构造(Date()为匿名对象(临时对象),生命周期只在当前一行)

	//Date d1;

	//fun1(d1);//一次构造,一次拷贝

	//d1 = fun2();//构造两次,拷贝一次,赋值一次

	//fun1(Date());//一次构造(Date()和d均为临时变量,合二为一,减少对象创建)

	//Date d = fun2();//一次构造,一次拷贝(生成临时变量,立即拷贝给b,合二为一)

	//Date d = fun3();//一次构造

	system("pause");
	return 0;
}

#include <iostream>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}

	Date(const Date& d)
	{
		cout << "Date(const Date& d)" << endl;
	}

	Date& operator=(const Date& d)
	{
		cout << "Date& operator=(const Date& d)" << endl;
		return *this;
	}

	~Date()
	{
		cout << "~Date()" << endl;
	}

	void Show()
	{}
};

void fun1(Date& d)
{}

Date& fun2()
{
	Date ret;
	return ret;
}

Date& fun3()
{
	return Date();
}

int main()
{
	//Date().Show();//一次构造

	//Date d1;

	//fun1(d1);//一次构造

	//d1 = fun2();//构造两次,赋值一次

	//fun1(Date());//一次构造

	//Date d = fun2();//一次构造,一次拷贝(出函数域进行的拷贝)

	//Date d = fun3();//一次构造,一次拷贝

	system("pause");
	return 0;
}

只有传参和传返回值会进行优化

例题:

//Test1中调用了_2_次AA的拷贝构造函数,_1_次AA的赋值运算符函数的重载
//Test2中调用了_2_次AA的拷贝构造函数,_0_次AA的赋值运算符函数的重载
//Test3中调用了_3_次AA的拷贝构造函数,_0_次AA的赋值运算符函数的重载
#include <iostream>
using namespace std;

class AA
{
	AA f(AA a)
	{
		return a;
	}
	void Test1()
	{
		AA a1;
		a1 = f(a1);
	}
	void Test2()
	{
		AA a1;
		AA a2 = f(a1);
	}
	void Test3()
	{
		AA a1;
		AA a2 = f(f(a1));
	}
};

struct类

默认访问限定符: public


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值