C++面向对象的三大特征为:封装、继承、多态
封装
类和对象
class Circle{
public :
//属性
int m_r;
//行为
double calculateZC(){
return 2*pi*m_r;
}
}
int main(){
Circle c1;
c1.m_r =10;
cout<< c1.calculateZC();
}
访问权限
- public 类内,类外都可以访问
- protected 类内可以访问,类外不可以访问 儿子可以访问父亲种保护的内容(与继承有关)
- private 类内可以访问,类外不可以访问
class Person{
public:
string m_Name;
proteceted:
string m_Car;
private:
int m_Password;
public :
void func(){
}
}
struct 和class的区别
class默认权限是private
struct默认权限是public
私有化的好处
- 自己控制读写权限(通过是否存在set和get函数)
- 对于写可以检测数据的有效性
在这里使用c1和c2的&是为了防止再拷贝一份数据。
如何把类单独写一个文件
Point.h如下:
#pragma once
#include <iostream>
using namespace std;
class Point
{
private:
int X;
int Y;
public:
int getX();
int getY();
};
Point.cpp如下:
只写函数的具体实现即可。要加Point::命名空间
#include "Point.h"
int Point::getX() {
return X;
}
int Point::getY() {
return Y;
}
构造函数和析构函数
构造函数可以无参,也可以有参。可重载。
拷贝构造函数
Person(const Person &p){
m_Age =p.age;
}
匿名对象 Person(10) 调用后会立刻被回收,因为没有给他名字。
使用时机
- 使用一个已经创建好的对象给新的对象传值。
- 实参传给形参的时候会调用拷贝构造函数
- 值返回局部对象
Person doWord(){
Person p1;
return p1;
//此时return 的不是p1,而是p1拷贝之后的副本。
}
构造函数调用规则
- 编译器添加三种函数:
- 构造函数
- 析构函数
- 拷贝构造 (值拷贝)
- 如果提供了有参构造,那么编译器就不会提供默认的空构造。仍会提供拷贝构造。
- 如果提供拷贝构造,编译器不会提供无参构造
深拷贝和浅拷贝
析构函数作用:
将堆区开辟得数据做释放操作
编译器提供的拷贝构造函数,会做浅拷贝的操作。浅拷贝会使两个对象指向同一块堆区
浅拷贝带来的问题,堆区的内存重复释放。
解决办法
重写拷贝构造函数。
Person(const Person &p){
m_Age = p.age;
//m_height = p.height//这个是编译器默认的拷贝构造函数
m_height = new int (*p.m+Height);
}
总结
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
初始化列表属性
主要作用是赋初值
可以通过这个赋值
构造和析构顺序问题
当一个类的对象作为另一个类的成员是,顺序是怎么样的?
构造顺序:先构造类的对象,再构造自身。
析构顺序:析构的顺序和构造的顺序相反。
静态成员
静态成员函数
- 所有对象共享一个函数
- 静态成员函数只能访问静态成员变量
两种调用方法:
不需要创建对象也可以访问,因为所有对象共享一个函数。
为什么不可以访问非静态成员?因为函数是共享的,不可以区分变量是哪个对象的。
C++对象模型this指针。
空对象也会被分配一个字节的空间.空对象也需要区分。
class Person{
}
Person p;
sizeof(p)//结果为1
如果不是空的。就看类里面的就可以。
class Person{
int m_A;
static int m_b;//不属于类的对象里面。
void func(){}//非静态成员函数 不属于类的对象上
static void func2(){}//静态成员函数,不属于类的对象上。
}
Person p;
sizeof(p)//结果为4
结论
只有非静态成员变量属于类的对象。(看对象的大小)
this作用
- 解决名称冲突
this 指针指向 被调用的成员函数 所属的对象
class Person{
public:
Person(int age){
//age=age; //这里的age指的是形参,不会去修改类内的age
this->age = age;
}
private:
int age;
}
- *this的使用
class Person{
public:
Person(int age){
//age=age; //这里的age指的是形参,不会去修改类内的age
this->age = age;
}
Person & PersonAdd(Person p){//前面的引用一定要用,不然就是新的变量,p2` , p2``等,不可以链式的修改p2了。
this->age += p.age;
return *this;//此时this指向的是p2。*this可以返回p2。
}
private:
int age;
}
p2.PersonAdd(p1).PersonAdd(p1).PersonAdd(p1);
const修饰成员函数(常函数)
mutable的作用
成员函数后面加const,修饰的是this指向,让指针指向的也不可以修改。
void showPerson()const{
m_A=100;//此行报错
this->m_b = 100;//有mutable ,就可以修改了
}
int m_A;
mutable int m_B;
常对象
const Person p;
p.m_A=100;//此行报错
p.m_b = 100;//在类种属性声明时有mutable ,就可以修改了
常对象只能调用常函数。因为常对象不可以调用普通成员函数,因为普通的成员函数可以修改成员属性。
友元函数
正常情况不可访问类种的私有属性,友元函数可以访问。
共三种;
- 全局函数做友元,(在类的开始加一个函数的friend声明)
- 类做友元
全局函数做友元
class Building{
fried void visitRoom(Building *buidling);//全局函数做友元
public :
string sittingRoom;
private:
string bedRoom;
}
void visitRoom(Building *buidling){
building->sittingRoom;
building->bedRoom;//正常来说不行
}
类做友元
在类的开始的时候写
friend class Person;//此时perSon就可以访问Building类
class Building{
friend class Person;
}
成员函数做友元
位置都是相同的, 需要加一个声明,在Person类里面的成员函数。
friend void Person::visit();
运算符重载
全局函数:Person p3 = p1+p2;
Person operator+ (Person &p1,Person &p2){
Person temp;
temp.m_A = p1.m_A + p2.m+A;
return temp;
}
成员函数:Person p3 = p1+p2;
Person operator+ (Person &p1){
Person temp;
temp.m_A = p1.m_A + this->m_A;
return temp;
}
左移运算符重载
作用:可以自定义输出
只能利用全局函数重载左移运算符。
cout<<属于ostream中的一个对象。返回ostream对象可以实现链式调用。
ostream & operator<<(ostream&cout,Person &p){
cout<<p.m_A<<p.m_b;
return cout;
}
仿函数
class MyAdd{
public:
int operator()(int num1,int num2){
return num1+num2;
}
}
MyAdd myadd;
myadd(1,2);