1、过程性编程和面向对象编程
1、 过程性编程思想:把问题拆分成一步步来完成,是自顶向下逐步求精,其最重要的是模块化的思想方法。
2、 面向对象编程思想:把问题看作一个个对象,然后由对象之间分工合作。
总结:面向对象是以对象功能来划分问题,而不是步骤(描述对象的数据和对数据交互所需的操作);
2、抽象和类
2.1、什么是类型:
在程序中指定的数据类型,表明:
1、 决定数据对象需要的内存数量;
2、 决定如何解释内存中的位;
3、 决定可使用数据对象执行的操作或方法;
总结:因此在C++中自己定义数据类型的同时,需要提供处理这些数据类型的方法。
2.2、什么是类:
1、 类是一种将抽象转换为用户定义类型的C++工具,它将数据表示和操纵数据的方法组合成一个整洁的包。
2、 类的组成:类声明 + 类方法定义。
类声明:数据成员和成员函数(其中方法称为公有接口),一般放在头文件中;
类方法定义:如何实现类成员函数;一般在源代码文件。
2.3、访问控制:
使用关键字:public、private,描述了对类中的成员的访问控制。
1、 public表示公有成员(成员函数)
2、 private表示私有成员(数据成员)------不加任何关键字时,为默认访问控制
一般我们将类数据成员设为private,而成员函数设置为public,这是因为使用类对象的程序可直接访问公有部分,而不能访问私有部分,
将成员函数设置为public,使得只能通过成员函数对数据成员进行操作,可以防止程序直接对数据操作,此时公有成员函数成了程序和对象
私有数据成员之间的桥梁。
公有成员函数可以访问对象的私有成员,将数据封装到私有部分,可以保护数据的完整性,这成为数据隐藏。
2.4、类的使用:
由上述2.2可知,类声明和类成员函数定义是分开的,那么类成员函数定义该如何写?
1、类声明:
class Student 习惯首字母大写
{
private:
string name;
string address;
int age;
public:
void Show_addr();
}; 注意结尾有分号
2、类成员函数定义: 这里使用作用域解析运算符::,表明该函数属于类Student
void Student::Show_addr()
{
//operation
}
3、如何调用类的公有成员函数(使用成员运算符)
Student fhl; 创建类对象
fhl.Show_addr(); 调用成员函数
4、不同类的类数据成员和成员函数都占据空间嘛?
不是的,创建的类对象都有自己的存储空间,但类对象的数据成员分别占据不同的内存块,而所有类对象共享同一组类方法。
Student fhl, wy; 创建了两个Student对象
fhl.name 和 wy.name 占据不同内存块
fhl.Show_addr() 和 wy.Show_addr() 指向同一个代码块,只是这些代码用于不同的数据
3、构造函数和析构函数
3.1、为什么需要构造函数和析构函数?
我们创建类对象的初衷是为了像其他类型一样(int、struct、char),但是到目前为止,还不允许下列操作:
class Student
{
private:
string name;
string address;
int age;
public:
void Show_addr();
};
int value = 10; valid
Student fhl = {"xiaofang",23,"hubei"}; invalid,因为前面提过,不允许直接访问数据成员
正如上面所说,不允许我们直接对数据成员操作,因此需要设计合适的成员函数,才可以对对象初始化,这就是构造函数。
构造函数:构造类对象时,自动调用一次构造函数,将值赋给数据成员来完成初始化
1、 构造函数的声明和定义:构造函数和类名一致
构造函数的定义可以放在类声明的公有部分,也可以放在外部(此时需要加上 类名::类名)
放在公有部分时:
class Student
{
private:
string name;
string address;
int age;
public:
构造函数的声明,可以放在公有部分,无返回值,可以使用默认参数
Student(const string t_name, string t_addr, int t_age = 26);
void Show_addr();
};
构造函数的定义:在类声明的外部使用::
Student::Student(const string t_name, string t_addr, int t_age)
{
name = t_name;
address = t_addr;
age = t_age;
}
2、构造函数的声明和定义放在一起
class Student
{
private:
string name;
string address;
int age;
public:
构造函数的声明,可以放在公有部分,无返回值,可以使用默认参数
Student(const string t_name, string t_addr, int t_age)
{
name = t_name;
address = t_addr;
age = t_age;
}
void Show_addr();
};
3.2、构造函数的使用
class Student
{
private:
string name;
string address;
int age;
public:
构造函数的声明,可以放在公有部分,无返回值,可以使用默认参数
Student(const string t_name, string t_addr, int t_age = 26);
void Show_addr();
};
构造函数的定义:在类声明的外部使用::
Student::Student(const string t_name, string t_addr, int t_age)
{
name = t_name;
address = t_addr;
age = t_age;
}
1、 显示调用构造函数
Student fhl = Student("xiaofang","hubei",26);
2、 隐式调用构造函数
Student fhl("xiaofang","hubei",26);
3.3、默认构造函数
1、什么是默认构造函数?
在未提供显示初始值时,用来创建对象,也就是说如果我们没有提供任何的构造函数,在创建对象时,C++将自动提供构造函数,只是这个构造函数为空实现。
当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数,但当定义了构造函数后,必须提供一个默认构造函数,也就是说一般提供两个构造函数:一个是默认构造函数(不带任何参数);一个是自己定义的带参数的构造函数,这符合函数重载的条件。
2、为什么需要默认构造函数?
主要是为了禁止创建未初始化的对象
默认构造函数的定义方法:
1、 所有参数为默认值
class Student
{
private:
string name;
string address;
int age;
public:
Student(const string t_name = "temp_name", string t_addr = "temp_addr", int t_age = 0);
void Show_addr();
};
Student::Student(const string t_name, string t_addr,int t_age)
{
name = t_name;
address = t_addr;
age = t_age;
}
2、 不带任何参数
class Student
{
private:
string name;
string address;
int age;
public:
Student();
void Show_addr();
};
Student::Student()
{
name = "temp_name";
address = "temp_addr";
age = 0;
}
上述默认构造函数选择一种使用即可,在有了上述的构造函数之后,就可以只创建对象,而不初始化,因此此时会自动调用默认构造函数完成初始化
Student fhl; 隐式调用默认构造函数
Student fhl = Student(); 显示调用默认构造函数
3.4、析构函数
1、什么是析构函数?
构造函数是创建对象,而析构函数就是对象过期时,使用析构函数来清理对象,同样也是自动调用。
1、 析构函数语法:~类名(); 无返回值和参数,无法重载
2、 由于析构函数不承担重要的工作,因此可以为空实现
Student::~Student()
{
}
3.5、析构函数的使用
1、由上面分析可知,析构函数是对象过期时,来清理对象的,但是我们需要搞清楚析构函数的调用顺序。
2、初始化和赋值操作的区别。
class Student
{
private:
string name;
string address;
int age;
public:
Student(const string t_name, string t_addr, int t_age); 普通构造函数
Student(); 默认构造函数
~Student(); 析构函数
void Showmessage();
};
Student::Student(const string t_name, string t_addr, int t_age)
{
name = t_name;
address = t_addr;
age = t_age;
}
Student::Student()
{
name = "temp_name";
address = "temp_addr";
age = 0;
}
Student::~Student()
{
cout << "Bye, " << name << endl;
}
1、 对于我们创建的局部对象来说,也是放在栈中的,因此符合后创建先删除
2、 初始化可能创造临时对象,而对已有对象的赋值操作会创建临时对象
int main()
{
1、初始化
Student fhl = Student{"xiaofang", "hubei1", 26}; 先创建,后析构
Student wy = Student{"xiaowu", "hubei1", 24}; 后创建,先析构
2、赋值操作
fhl = Student("dafang", "beijing", 25); 对已有对象的再此赋值操作,会在赋值前有一个创建临时对象的操作。
return 0;
}
result:
bye,dafang
bye,xiaowu
bye,dafang
3.6、其他知识点
1、对象初始化的几种方法:
class Student
{
private:
string name;
string address;
int age;
public:
Student(const string t_name, string t_addr, int t_age); 普通构造函数
Student(); 默认构造函数
~Student(); 析构函数
void Showmessage();
};
Student::Student(const string t_name, string t_addr, int t_age)
{
name = t_name;
address = t_addr;
age = t_age;
}
Student::Student()
{
name = "temp_name";
address = "temp_addr";
age = 0;
}
Student::~Student()
{
}
1、 Student fhl = Student{"xiaofang", "hubei1", 26}; 普通构造函数的显示调用
2、 Student fhl{"xiaofang", "hubei1", 26}; 普通构造函数的隐式调用
3、 Student fhl = {"xiaofang", "hubei1", 26}; 普通构造函数的列表初始化方法
4、 Student fhl = Student(); 默认构造函数的显示调用
5、 Student fhl; 默认构造函数的隐式调用
2、const对象和成员函数
const Student fhl = Student{"xiaofang", "hubei1", 26}; const对象
fhl.Showmessage(); 不被允许,因为Showmessage函数可能修改const对象
解决办法:将const放在函数()的后面
class Student
{
private:
string name;
string address;
int age;
public:
Student(const string t_name, string t_addr, int t_age); 普通构造函数
Student(); 默认构造函数
~Student(); 析构函数
void Showmessage() const; const成员函数
};
void Student::Showmessage() const
{
cout << "姓名为:" << name << endl;
cout << "地址为:" << address << endl;
cout << "年龄为:" << age << endl;
}
3.7、构造函数和析构函数小结
一、构造函数语法:类名(){}
1. 构造函数,没有返回值也不写void
2. 函数名称与类名相同
3. 构造函数可以有参数(也可以没有参数),因此可以发生重载
4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法: ~类名(){}
1. 析构函数,没有返回值也不写void
2. 函数名称与类名相同,在名称前加上符号 ~
3. 析构函数不可以有参数,因此不可以发生重载
4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
构造函数允许函数重载,允许有多个构造函数存在:默认构造函数、普通构造函数
析构函数不允许重载,有且仅有一个
构造函数使用了new,则在析构函数中必须有delete