c++面向对象语言基础
类和对象
类:数据类型
对象:数据类型的一个实例
如何创建自己的类:
class student
{
string name;
int age;
};
如何使用一个类
student aa;
aa.age=20; //不规范
公有和私有的概念:
class student
{
public:
string name;
private:
int age;
}
public:外部可读写
private:外部无法访问
可以只有public
公有和私有(加强版)
private目的:封装
class student
{
public:
string name;
void print_name(); //公有方法
private:
int age;
void print_age(); //私有方法
}
void student::print_name()
{
cout<<"张三";
}
void student::print_age()
{
cout<<age;
}
//主函数
student aa;
aa.print_name();
aa.print_age(); //报错,提示函数不存在
只有通过公有方法的外壳调用私有的方法
如把上面的代码修改:
class student
{
public:
string name;
void print_name(); //公有方法
private:
int age;
void print_age(); //私有方法
}
void student::print_name()
{
print_age();
}
void student::print_age()
{
cout<<age;
}
//主函数
student aa;
aa.print_name();
//此时可以正确打印出年龄;
正规程序定义的写法:
class student
{
public:
void print_age();
void print_name(); //公有方法
private:
int age;
string name;
}
把数据都放在private中,把方法都放在public中
起到防错机制的效果
成员函数的重载
函数名称一样,靠传入参数的差异进行区分调用
class student
{
public:
int age;
string name;
bool set(int a);
bool set(string a);
}
student::set(int a)
{}
student::set(string a)
{}
n
构造函数
上述set为成员普通方法,还存在部分特殊方法。如:构造函数;
对student aa
创建aa对象,aa没有默认值;
如果存在构造函数,则aa有默认值;
构造函数本质即对象的初始化
//不带参数的构造函数
class student
{
public:
int age;
string name;
student();
};
student::student()
{
age = 20;
name = "zhangsan"
}
//主函数中
student aa; //自动调用构造函数,有默认值
//带参数的构造函数
class student
{
public:
int age;
string name;
student();
student(int a, string b); //同名构造函数重载
};
student::student(int a, string b)
{
age = a;
name = b;
}
student::student()
{
age = 20;
name = "zhangsan"
}
//主函数中
student aa; //自动调用构造函数,有默认值
student bb(22,"李四");
析构函数(正规C++程序、大型)
使用析构函数销毁内存数据
- 对象aa被创建之后,数据一直存放在内存中。这块内存什么时候被释放?调用析构函数的时候!
- 主函数中不能直接运行
student aa; delete aa;
- 只有
student *p = new student(20."张三"); delete p;
才将自动调用析构函数 - delete时调用
- 内存中自动删除
写法和构造函数相似
student::`student()
{
}
//无输入,无返回,可以没有内容,也可以cout<<"delete object";
常成员函数 const
//安全,不能修改数据
class student
{
public:
int age;
string name;
bool set(int i);
bool read() const; //常成员函数,表示这个函数只读不写
};
bool student::read() const
{
cout << name;
cout << age;
return true;
}
静态成员 static
静态成员数据据
如student类中需要一个sum来统计所有学生类成员的数量
需要一个变量sum,与student类有关,但与它的任何一个对象都无关
这种描述全局,但是与某个对象属性无关的,叫做静态成员数据
读取静态成员数据的方法叫做 静态成员函数
class student
{
public:
int age;
string name;
student();
static int sum; //static表示静态成员数据
static int count(); //static表示静态成员函数,返回有多少个对象
};
student::student() //
{
age = 20;
name = "zhangsan"
sum = sum + 1;
}
int student::count() //静态成员函数的定义,注意不加static
{
return sum;
}
//只有通过静态成员函数,才能访问静态数据
使用静态成员函数
student aa;
student bb;
aa.count();
bb.count();
student::count();
//上述三种count函数使用方式结果完全一致,只有静态函数可以这样
类的派生与继承
什么是派生:
- 学生类划分为研究生和本科生
- 学生是父类/基类/超类,本科生和研究生是子类
- 所谓派生,是相对父类而言
- 所谓继承,是相对子类而言
派生本科生的写法:
class undergraduate : public sudtent
{
public:
string course; //新增定义,学生类中没有
}
派生研究生的写法:
class postgraduate : public sudtent
{
public:
string research; //新增定义,学生类中没有
}
没有写name和age,因为子类自动继承
拥有属于自己的新增定义
子类没有构造函数,如何创建它的对象?name和age是否还有值?
postgraduate bb;
bb.set(25); //调用了父类的构造方法
类在不同情况下的继承
public,private,protect
在没有继承与派生的情况下,protect和private效果完全一样
研究生类定义 class postgraduate : public sudtent
为什么要加上public?
public表示公有继承,只继承student的公有部分{name,age}
并且,age和name也将继续作为研究生类的public部分
研究生类是无法继承也无法访问student类的私有部分
- 当把父类student类定义的private替换为protect
- 对student本身没有变化
- 当
class postgraduate : public student{}
时,不仅public部分,protect部分也会被继承 - student的protect部分也被继承到了postgraduate的protect部分
- 继承总结
- 私有继承:把
class postgraduate : public student{}
中public换成private - 保护继承:把
class postgraduate : public student{}
中public换成protect
- 私有继承:把
父类student有的 | 子类postgraduate继承到的 | |
---|---|---|
公有继承 | public A; private B; | public A |
公有继承 | public A; private B; | public A; private B; |
私有继承 | public A; private B; | private A; |
保护继承 | public A; protect B; | protect A,B; |
保密优先级:private > protect > public
子类的构造函数
子类可以与自己的构造函数:
子类没有构造函数,系统会调用弗雷德构造函数
无论子类有没有自己的构造函数,父类的构造函数都要被调用
研究生不带参数构造函数:
class postgraduate : public sudtent
{
public:
string research; //新增定义,学生类中没有
postgraduate();
postgraduate(int a, string b, string c);
}
postgraduate::postgraduate() //无参构造
{
research = "asic design";
}
//主函数
postgraduate bb;
创建子类bb对象时,系统会首先自动运行父类的student构造函数,对age和name进行赋值,随后运行子类postgraduate构造函数,对bb对象进行拓展,增加research内容
子类不能继承父类的构造方法,只能调用!
研究生带参数构造函数:
class postgraduate : public sudtent
{
public:
string research; //新增定义,学生类中没有
postgraduate();
postgraduate(int a, string b, string c);
}
postgraduate::postgraduate() //无参构造
{
research = "asic design";
}
postgraduate::postgraduate(int a, string b, string c):student(a, b) //带参构造函数的定义
{
research = c;
}
//主函数
postgraduate bb(25, "李四", "ASIC design");
程序会先调用父类student的构造函数,把25和李四两个值传入父类student带参数的构造函数中
随后把参数c = asic design传给bb自己的research属性,实现对研究生对象的全动态初始化赋值
多态
多态:方法可以不专属于某个类,例如对两个字符串也可以使用加法,而非只有int类
c++中实现上述有三种方法:
- 重载
- 隐藏
- 覆盖(override,又叫重写,这个才是c++的多态)
重载:
重载的两个函数的参数格式必须完全不一样
隐藏和覆盖的同名函数的参数可以完全一样
不依赖于面向对象,依赖于编辑器
隐藏:
在父类和子类中都定义一个study()函数,输入的参数格式可以不同
class student
{
public:
void study(bool a) {cout<<"好好学习";}
}
class postgraduate
{
public:
void study(int b) {cout<<"芯片设计";}
}
//主函数
postgraduate bb;
student aa;
bb.study(2); //成功
aa.study(true); //成功
bb.study(true); //失败,没有重载study(),不能根据传入参数不同调用不同函数,因为这里的父类study被隐藏了(没有被覆盖)
类指针
类指针与结构体指针大致相同
student *p;
student aa;
p = &aa;
p -> name;
p -> study(); // 对aa执行成员函数
//更高级的写法
student *p = new student(20,"张三");
delete p; //调用析构函数
将继承加入到类指针中:
student *p1;
postgraduate *p2;
student aa;
postgraduate bb;
p1 = &aa;
p2 = &bb;
p1 = &bb;
p2 = &aa; //报错
父类指针可以指向子类成员,反之不行
研究生一定是学生,学生不一定是研究生
注意: 虽然使用student指针指向postgraduate对象,但不能调用postgraduate中的属性和方法,父类指针指向子类时,仍然只能使用父类的方法
那么如何解决这个问题呢??
真正的多态与虚函数
分别在student,undergraduate,postgraduate中建立一个study方法
- 都叫study,内容不一样
- 在所有子类父类study的声明前面加上关键字virtual,表示这是一个虚函数
class student
{
public:
virtual void study();
}
void student::study(){cout<<"我是学生";}
class postgraduate
{
public:
virtual void study();
}
void student::postgraduate(){cout<<"我是研究生";}
class undergraduate
{
public:
virtual void study();
}
void student::undergraduate(){cout<<"我是本科生";}
//主函数
postgraduate bb;
student aa;
undergradute cc;
student *p;
p = &aa;
p -> study(); //调用学生类的study
p = &bb;
p -> study(); //调用研究生类的study
p = &cc;
p -> study(); //调用本科生类的study
注意:他们的输入参数都是没有参数,这里与重载就不一样了!!因此调用哪一个根据指向的类动态调用
重载时编译时决定的,多态是运行时决定的
纯虚函数与抽象类
抽象类:如果把大学生划分为本科生和研究生,那么一个学生要么属于本科生,要么属于研究生,不能单独属于学生这一类,那么学生就是抽象类
设置抽象类依赖于纯虚函数:
class student
{
public:
student();
student(int a, string b);
virtual void study() =0;
}
//主函数
student aa;
student *p;
postgraduate bb;
p = &bb;
p -> study();
virtual void study() =0;
中声明study方法,注意virtual和=0,意为纯虚函数- 此时study()函数只有声明,没有定义,其具体内容靠子类的多态来实现
- 依然可以创建student类型指针,但不能创建该类型对象
- 注: 可能存在多个virtual函数,至少要将一个 =0,指明其为纯虚函数