一:知识点
1.先构造的后析构,后构造的先析构
2.拷贝构造,同样也是一个构造函数,Person(Person & src){};与构造函数是重载的关系
下面两种方式都是拷贝构造
Person p2(p1);
Person p2 = p1;
拷贝构造会在使用同类型的对象构造新对象的时候自动调用
如果没有自己实现拷贝构造,编译器会自动生成一个浅拷贝的拷贝构造函数
拷贝构造要防止浅拷贝
原因:浅拷贝会导致两个指针指向同一块堆上的空间,当析构的时候,第二次析构会导致程序崩溃(重复释放一块堆上的空间会导致崩溃),所以要深拷贝
拷贝构造要防止自赋值:给野指针复制是错误的
(面试常问)拷贝构造必须传引用,否则会导致死递归,原因是重复使用同类型的对象构造新对象
Person(Person src)
{}
Person p1("zhangsan",23,1);
Person p2(p1);//用p1拷贝构造p2
当用p1拷贝p2的时候,需要将实参传给实参Person src(p1),又是拷贝构造,然后再把实参传给形参Person src(p1),这样就会导致死递归
3.等号运算符重载
使用同类型的对象给同类型的对象赋值的时候,会自动调用等号运算符重载
如果没有自己实现等号运算符重载,编译器会自动生成一个浅拷贝的等号运算符重载
4.类中的权限
public(公共的):在任何地方都能访问,在类内类外都可以访问
private(私有的):只能当前类中进行访问
放在public里面还是private里面并没有明确的规定,但是成员变量一般都放在private,函数放在public里面,如果某些函数不希望外界调用,就可以放在private里面
5.strcut和class的区别
在C++中,struct和class都是类,只不过struct里面的默认属性是public,class里面的默认属性是private
6.内置类型可以用等号连接,自定义类型正常来说不可以用等号连接,没有连接的规则
7.有的说面向对象是三大特征:①封装②继承③多态
有的说面向对象是四大特征:①封装②继承③多态④抽象
8.常量必须初始化,编译期的时候会把用到常量的地方会自动替换为常量的值,如果不初始化,后面就不能再赋值了
9.初始化列表:是在构造函数即将执行的时候执行的操作
10.引用也必须初始化,引用的底层是一个指针,所有用到引用的地方都会替换为指针的解引用,初始化之后就无法改变引用的指向了,也就不能再赋值了
11.如果类中存在必须初始化的成员变量(如常量、引用等),就必须手动写到初始化列表
12.static定义的变量存储在数据段
13.静态成员,一个类只有一份,整个类所共用的,其他成员变量,每个对象都有一份
静态成员,必须在类外进行初始化
静态成员的访问不依赖于this指针,所以不依赖于对象,可以通过作用域访问静态函数
静态的成员方法里面只能用静态成员,因为没有this指针,所有的成员变量都使用不了
14.::表示作用域,通过作用域访问没有this指针
通过作用域可以访问静态成员变量和静态成员方法
15.(面试重点)注意:不能把常量的地址泄露给非常量的指针,这是一个非常严重的问题
16.常对象只能调用常方法,原因:不能把常量的地址泄露给非常量的指针
常对象调用静态方法是可以的
静态方法一般不适用对象调用,一般使用作用域调用
17.什么时候需要把方法写成const?
方法内不会修改成员变量的就写成const
不一定修改的就提供两个,让他们重载
18.单例模式:
19.delete NULL;和free(NULL);都是对的,不要delete、free野指针
20.构造函数(拷贝构造也算是构造函数)和析构函数不允许使用类型限定符(如const)
二:代码
//面向对象
#include <iostream>
using namespace std;//命名空间
int school = 985;
int Person::_num = 0;//静态变量必须在类外进行初始化
class Person
{
private:
char* _name;
int _age;
const int _sex;//性别不会改变,所以加const
int& _school;//引用必须初始化
public:
static int _num;//静态成员,一个类只有一份,整个类所共用的,静态成员,必须在类外进行初始化
Person()//默认构造函数,没有参数,当然肯定有一个this指针,一般不说这个参数
:_sex(1),_school(school)//初始化列表,在调用构造函数之前执行
{
cout << "Person()" << endl;
_name = NULL;
_num++;
}
//构造函数
Person(const char* name, int age, int sex)
:_sex(sex), _school(school)//初始化列表
{
cout << "Person(const char *name,int age,int sex)" << endl;
_name = new char[strlen(name) + 1];
for (int i = 0; i < strlen(name) + 1; i++)
{
_name[i] = name[i];
}
_age = age;
_num++;
}
//拷贝构造在使用同类型的对象构造新对象的时候自动调用的
//如果没有自己实现拷贝构造,编译器会自动生成一个浅拷贝的拷贝构造函数
//浅拷贝:防止浅拷贝
//(***面试常问)拷贝构造必须传引用,否则会导致死递归,为什么?
// 原因:Person p2(p1),如果不是引用的话,就是Person (Person src)会将实参传给形参Person src(p1),
// 这又是一个拷贝构造,然后又把实参传给形参Person src(p1),就会导致死递归
//src传进来不会被改动,所以加一个const,也为了防止传进来的对象是一个常对象,如果不加const,就会导致把
//常量的地址泄露给非常量的指针,这是一个错误,所以要用常引用
Person(const Person& src)
:_sex(src._sex), _school(school)
{
cout << "Person(Person & src)" << endl;
_name = new char[strlen(src._name) + 1];//深拷贝
for (int i = 0; i < strlen(src._name) + 1; i++)
{
_name[i] = src._name[i];
}
_age = src._age;
_num++;
//_name = src._name;浅拷贝,两个类的_name都指向同一个堆上的空间,当第二次析构的时候会导致程序崩溃
//_age = src._age;
//_sex = src._sex;
}
//等号运算符重载
//是在使用同类型的对象给同类型的对象赋值的时候自动调用的
//如果没有自己实现,编译器会自动生成一个浅拷贝的等号运算符重载
//加一个const,也为了防止传进来的对象是一个常对象,如果不加const,就会导致把
//常量的地址泄露给非常量的指针,这是一个错误。所以要用常引用
Person& operator = (const Person& src)
{
cout << "void operator = (Person& src)" << endl;
//_name = src._name;//这是浅拷贝,会崩溃
//防止自己给自己赋值
//如果不加这条语句,在经过下面的delete[] _name之后,_name就变成了野指针
if (this == &src)//如果当前对象的指针和传进来的指针相同,说明指向的是同一个对象
{
return *this;
}
//防止内存泄露,之前的_name指向其他的地方了,经过下面的拷贝后,之前指向的地方就丢失了,
//所以要先释放之前的内存,防止内存泄露
delete[]_name;
_name = new char[strlen(src._name) + 1];
for (int i = 0; i < strlen(src._name) + 1; i++)
{
_name[i] = src._name[i];
}
_age = src._age;
return *this;
}
//提供公有的方法,可以让外界获取到信息
const char* get_name(/* const Person * this */)const
{
return _name;
}
char* get_name(/* Person * this */)
{
return _name;
}
int get_age(int is_police)
{
if (is_police)
{
return _age;
}
return 18;
}
void add_age()
{
_age++;
}
void Show()
{
cout << _name << " " << _age << " " << _sex << endl;
}
//析构函数
~Person()
{
cout << "~Person" << endl;
delete[]_name;
_num--;
}
};
int main()
{
//const Person p1 ("laoliu",23,1);
//p1.Show();
//Person p2(p1);//用p1来构造p2
//p2.Show();
//Person p2(p1);//拷贝构造
//Person p3 = p1;//也是拷贝构造
//p3 = p1;//cpp文件可以,c文件不可以
//内置类型可以用等号连接,自定义类型正常来说不可以用等号连接,没有连接的规则
//p3 = p3;
//p3.show();
//cout << Person::_num << endl;;
return 0;
}