类和对象
什么是对象
万物皆对象,任何一种事物都可以看做是对象。
如何描述对象
通过对象的属性(名词、数量词、形容词)和行为(动词)来描述对象。
例如:描述冰箱对象
- 属性:品牌、容量、颜色、功耗。。。。
- 行为:冷冻、冷藏、装东西。
面向对象编程
对自然世界中对象的观察引入到编程实践的一种理念和方法,这种方法称为“数据抽象”,即在描述对象时把细节的东西剥离出去,只考虑一般性的、有规律性的、统一性的东西。
什么是类
类是将多个对象的共性提取出来定义的一种新的数据类型,是对对象属性和行为的抽象描述。
现实世界到类到编程世界
具体对象–抽象–>属性/行为–实例化–>具体对象
类的定义和实例化
类的语法形式
struct/class 类名:继承方式 基类{
访问控制限定符:
类名(形参表):初始化表{...}//构造函数
~类名(void){...} //析构函数
返回类型 函数名(形参表){} //成员函数
数据类型 变量名;//成员变量
};
访问控制限定符:影响类中成员的访问位置
- public:公有成员,在任何位置都可以访问。
- private:私有成员,只有类的内部才能访问。
- protected:保护成员。
构造函数(constructor)
语法:
class 类名{
类名(构造形参表){
//主要负责初始化对象,即初始化成员变量
}
};
- 函数名与类名相同,没用返回类型。
- 构造函数在创建对象时自动被调用和执行,不能像普通的成员函数通过对象去调用。
对象的创建和销毁
- 在栈区创建单个对象
类名 对象(构造实参表);
类名 对象 = 类名(构造实参表);
- 在栈区创建多个对象(对象数组)
类名 对象数组[元素个数] = {
类名(构造实参表),类名(构造实参表),...};
- 在堆区创建/销毁单个对象
创建:类名* 对象指针 = new 类名(构造实参表);
销毁:delete 对象指针;
- 在堆区创建/销毁多个对象(对象数组)
创建:
类名* 对象指针 = new 类型[元素个数]{
类名(构造实参表),类名(构造实参表),...};
销毁:
delete[] 对象指针;
构造函数和初始化表
- 构造函数可以重载、可以带有缺省参数
- 缺省构造函数(无参构造函数)
- 如果一个类没用定义构造函数,编译器会提供一个缺省(无参)构造函数。(对基本类型的成员变量不做初始化,对类、类型的成员变量会自动调用相应的类的无参构造函数来初始化)
- 如果自己定义了构造函数,无论是否有参数,编译器都不会再提供缺省的构造函数了。
- 类型转换构造函数(单参构造函数)
class 目标类型{
[explicit] 目标类型(源类型){...}
};
可以实现源类型到目标类型的隐式转换。
注:使用explicit关键字,可以强制这种转换必须显式的完成。
- 拷贝(复制)构造函数
- 用一个已存在的对象构造同类型的副本对象时,会调用该类的拷贝构造函数。
- 如果一个类中没用定义拷贝构造函数,那么编译器会为其提供一个缺省的拷贝构造函数。(对基本类型成员变量按字节复制;对类类型成员变量(成员子对象),自动调用相应类型的拷贝构造函数,多数情况下不需要自己写拷贝构造函数,因为编译器缺省提供的拷贝构造函数已经很好使用了。)
- 拷贝构造函数调用时机:用已存在的对象作为同类对象的构造实参;以对象的形式向函数传递参数;从函数中反悔对象(可能被编译器优化掉)
class 类名{
类名(const 类名&){...}
};
eg:
class A{...};
A a1;
A a2(a1);//调用拷贝构造函数
- 初始化表
- 如果有类、类型成员变量(成员子对象),而该类又没用无参构造函数,则必须使用初始化表来初始化该成员变量。
class 类名{
类名(形参表):成员变量(初值),...{}
};
代码示例
- constructor.cpp
#include <iostream>
using namespace std;
class Student{
public:
Student(const string& name,int age,
int no){
cout << "构造函数" << endl;
m_name = name;
m_age = age;
m_no = no;
}
void who(void){
cout << "我叫" << m_name << ",今年"
<< m_age << "岁,学号是" << m_no
<< endl;
}
private:
string m_name;
int m_age;
int m_no;
};
int main(void)
{
//创建对象,实例化对象,构造对象
//(...),指明构造函数需要的实参
Student s("李辉",35,10011);
s.who();
//构造函数不能想普通成员函数一样去调用
//s.Student("李三",36,10012);
return 0;
}
- 执行结果
- clock.cpp
#include <iostream>
#include <cstdio>
#include <ctime>
#include <unistd.h>
class Clock{
public:
Clock(time_t t){
tm* local = localtime(&t);
m_hour = local->tm_hour;
m_min = local->tm_min;
m_sec = local->tm_sec;
}
void run(void){
while(1){
printf("\r%02d:%02d:%02d",
m_hour,m_min,m_sec);
fflush(stdout);//刷新输出缓冲区
if(++m_sec == 60){
m_sec = 0;
if(++m_min == 60){
m_min = 0;
if(++m_hour == 24)
m_hour = 0;
}
}
sleep(1);
}
}
private:
int m_hour;
int m_min;
int m_sec;
};
int main(void)
{
Clock c(time(0));
c.run();
return 0;
}
- 执行结果
- 02constructor.cpp
#include <iostream>
using namespace std;
class Student{
public:
Student(const string& name,int age,
int no){
cout << "构造函数" << endl;
m_name = name;
m_age = age;
m_no = no;
}
void who(void){
cout << "我叫" << m_name << ",今年"
<< m_age << "岁,学号是" << m_no
<< endl;
}
private:
string m_name;
int m_age;
int m_no;
};
int main(void)
{
//创建对象,实例化对象,构造对象
//(...),指明构造函数需要的实参
//Student s("李辉",35,10011);
//和上面等价
Student s = Student("李辉",35,10011);
s.who();
//栈区创建对象数组
Student sarr[3] = {
Student("白骨精",19,10012),
Student("林黛玉",18,10013),
Student("潘金莲",20,10014)};
sarr[0].who();
sarr[1].who();
sarr[2].who();
//在堆区创建单个对象
Student* ps =
new Student("貂蝉",17,10015);
ps->who();//(*ps).who()
delete ps;
ps = NULL;
//在堆区创建多个对象,c++11
Student* parr = new Student[3]{
Student("小乔",28,10016),
Student("大乔",29,10017),
Student("西施",30,10018)};
parr[0].who();//(*(parr+0)).who()
parr[1].who();
parr[2].who();
delete[] parr;
parr = NULL;
return 0;
}
- 执行结果
- defCons.cpp
#include <iostream>
using namespace std;
class A{
public:
A(void){
cout << "A的无参构造函数" << endl;
m_data = 12345;
}
int m_data;
};
class B{
public:
//B(int num){}
int m_num;//基本类型
A m_a;//类 类型(成员子对象)
};
int main(void)
{
B b;
cout << b.m_num << endl;//?
cout << b.m_a.m_data << endl;//12345
return 0;
}
- 执行结果
- castCons.cpp
#include <iostream>
using namespace std;
class Integer{
public:
Integer(void){
cout << "Integer(void)" << endl;
m_data = 0;
}
//int->Integer
//类型转换构造函数
/*explicit*/ Integer(int data){
cout << "Integer(int)" << endl;
m_data = data;
}
void print(void){
cout << m_data << endl;
}
private:
int m_data;
};
int main(void)
{
Integer i;
i.print();//0
//1)自动调用类型转换构造函数,用123作为
//构造实参创建一个临时对象
//2)再用临时对象给i进行赋值操作
i = 123;
i.print();//123
//使用类型转换构造函数实现的隐式转换代码
//可读性差,推荐使用显式转换
//i = (Integer)321;//C风格
i = Integer(321);//C++风格
i.print();//321
return 0;
}
- 执行结果
- cpCons.cpp
#include <iostream>
using namespace std;
class A{
public:
A(int data = 0){
cout << "A(int=0)" << endl;
m_data = data;
}
//拷贝构造函数
A(const A& that){
cout << "A(const A&)" << endl;
m_data = that.m_data;
}
int m_data;
};
int main(void)
{
const A a1(12345);
A a2(a1);
//A a2 = a1;//和上面写法完全等价
cout << a1.m_data << endl;
cout << a2.m_data << endl;
return 0;
}
- 执行结果
- 02cpCons.cpp
#include <iostream>
using namespace std;
class A{
public:
A(int data = 0){
cout << "A(int)" << endl;
m_data = data;
}
A(const A& that){
cout << "A(const A&)" << endl;
m_data = that.m_data;
}
int m_data;
};
class B{
public:
A m_a;//成员子对象
};
int main(void)
{
B b;
b.m_a.m_data = 12345;
B b2(b);//拷贝构造
cout << b.m_a.m_data << endl;//12345
cout << b2.m_a.m_data << endl;//12345
return 0;
}
- 执行结果
- 03cpCons.cpp
#include <iostream>
using namespace std;
class A{
public:
A(void){
cout << "A(void)" << endl;
}
A(const A& that){
cout << "A(const A&)" << endl;
}
};
void foo(A a){}
A bar(void){
A a;//无参
cout << "&a=" << &a << endl;
return a;//拷贝
}
int main(void)
{
A a1;//无参
A a2 = a1;//拷贝
foo(a1);//拷贝
/* 正常情况bar返回a拷贝到临时对象,临时
* 对象再拷贝到a3,发生两次拷贝;但是因
* 为编译器优化,让a3直接引用a,不再发生
* 拷贝*/
//去优化的选项
//g++ xx.cpp -fno-elide-constructors
A a3 = bar();//拷贝
cout << "&a3=" << &a3 << endl;
return 0;
}
- 执行结果
- initlist.cpp
#include <iostream>
using namespace std;
class Student{
public:
//先把成员变量定义出来(内存分配)
//再执行构函数函数体,赋初值
/*Student(const string& name,int age,
int no){
cout << "构造函数" << endl;
m_name = name;
m_age = age;
m_no = no;
}*/
//使用初始化表:定义成员变量同时初始化
Student(const string& name,int age,
int no):m_name(name),m_age(age),
m_no(no){}
void who(void){
cout << "我叫" << m_name << ",今年"
<< m_age << "岁,学号是" << m_no
<< endl;
}
private:
string m_name;
int m_age;
int m_no;
};
int main(void)
{
//创建对象,实例化对象,构造对象
//(...),指明构造函数需要的实参
Student s("李辉",35,10011);
s.who();
return 0;
}
- 执行结果
- 02initlist.cpp
#include <iostream>
using namespace std;
class A{
public:
A(int data){
cout << "A的构造函数" << endl;
m_data = data;
}
int m_data;
};
class B{
public:
//:m_a(..),显式指明成员子对象m_a的
//初始化方式
B(void):m_a(12345){
cout << "B的构造函数" << endl;
}
A m_a;
};
int main(void)
{
B b;
return 0;
}
- 执行结果
企业员工类emloyee - employee.cpp
#include <iostream>
using namespace std;
class Employee{
public:
void printInfo(void){
cout << "姓名:" << m_name << endl;
cout << "工号:" << m_id << endl;
cout << "基础工资:" << m_salary << endl;
}
void calSalary(void){
cout << "请输入出勤天数:";
int days;
cin >> days;
double basic = m_salary * (days/23.0);
double merit = basic / 2;
cout << "总工资:" <<
(basic + merit) << endl;
}
public:
void setId(int id){
if(id<10000)
cout << "无效的工号" << endl;
else
m_id = 10011;
}
void setName(const string& name){
if(name.size() > 20)
cout << "无效的姓名" << endl;
else
m_name = name;
}
void setSalary(double salary){
if(salary<0)
cout << "无效的工资" << endl;
else
m_salary = salary;
}
private:
int m_id;//工号
string m_name;//姓名
double m_salary;//工资
};
int main()
{
Employee emp;
emp.setId(10011);
emp.setName("张三");
emp.setSalary(6600);
emp.printInfo();
emp.calSalary();
return 0;
}
- 执行结果