一、类和对象
概述:C++中最核心的知识点莫过于类和对象,也就是面向对象编程,以及相较于c语言的各种容器,便捷的语法和实现,但同样的也会带来学习上的困难,本文仅供记录个人学习c++的历程,难免有错误和疏漏,欢迎大家指出,与各位共勉
1.类和对象-封装-对象特性
按照不同类别的属性和行为看作一个整体,形成一个类,例如人类,属性有性别,年龄,身高,体重等,行为有吃饭,走路,跑步,说话等。车类,属性有轮胎,发动机,方向盘。行为有载人,放歌等,诸如此类的概念都可以抽象为类来理解,大体的实现代码如下
#include <iostream>
#include <string.h>
using namespace;
class People
{
protected: //私有权限,但子类可以访问
private: //私有权限,仅有类内可以访问
public: //公共权限,类内和类外都可以访问
//构造函数:分为有参和无参构造
//以及拷贝构造函数
People()
{
cout << "People无参构造函数调用" << endl;
}
People(int a)
{
cout << "People有参构造函数调用" << endl;
}//拷贝构造函数
People(const People &p)
{
//类似把另一个一个实例化对象的某些或全部属性或行为拷贝到当前新创建的对象上
name = p.name;
gender = p.gender;
age = p.age;
}
~People()
{
//若有在堆区创建的内存在这里要释放,涉及深拷贝
cout << "People析构函数调用" << endl;
}
//成员函数(方法)
void set(int a){}
int get(void){}
//成员属性
string name;
int gender;
int age;
//......
};
void test()
{
// 1. 括号法
People p1; //无参构造
People p2(10); //有参构造
People p3(p2); //拷贝构造函数调用
// 2. 显示法
People p1; //无参构造
People p2 = People(10); //有参构造
People p3= People(p2); //拷贝构造函数调用
// 3. 隐式转换法
People p4 = 10; //有参构造
People p5 = p4; //拷贝构造函数调用
//注意事项
//匿名对象 特点: 当前行执行结束后,系统会立即回收掉匿名对象
//不要用拷贝构造函数初始化匿名对象 编译器会认为 People(p3) == People p3;
//也不要直接People p1();编译器会认为这是类函数的声明。
}
#include <iostream>
class Person
{
public:
Person(int age, int Height)
{
std::cout << "有参构造函数调用!" << std::endl;
this->m_age = age;
this->m_Height = new int(Height);
}
Person(const Person &p)
{
//自己实现在堆区申请空间,创建变量
this->m_age = p.m_age;
this->m_Height = new int(*(p.m_Height));
}
~Person()
{
//释放堆区内存
if(m_Height != NULL)
{
delete m_Height;
m_Height = NULL;
}
std::cout << "析构函数调用!" << std::endl;
}
int m_age;
int *m_Height;
};
void test01()
{
Person p1(18, 182);
Person p2(p1);
std::cout << "年龄: " << p1.m_age << "身高: " << *p1.m_Height << std::endl;
std::cout << "年龄: " << p2.m_age << "身高: " << *p2.m_Height << std::endl;
}
int main()
{
test01();
return 0;
}
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
4.2.6.7 类中类
当类中成员是其他类对象时,我们称该成员为 对象成员,构造顺序: 先调用对象成员的构造函数,再调用本类对象的构造函数,析构顺序与构造相反
class Phone
{
private:
string Name;
public:
Phone(string PName)
{
Name = PName;
}
~Phone()
};
class Person
{
public:
//Phone m_phone = Name; 隐式转换
Person(string name, string Name): m_Name(name), m_phone(Name)
{}
private:
//姓名
string m_Name;
//手机牌子
Phone m_phone;
};
4.2.6.8 静态成员
1. 所有对象都共享同一份数据
2. 编译阶段就分配内存
3. 类内声明,类外初始化
4.静态成员函数
(1) 所有对象共享同一个函数
(2)静态成员函数只能访问静态成员变量
4.3 C++对象模型和this指针
4.3.1成员变量和成员函数分开存储
4.3.2 this指针概念
class Person
{
public:
Person(int age)
{
//this 指针是一个指针常量 Person * const this;无法改变其指向,但是可以更改指向的值
this->age = age;
}
//此处用&确保返回还是p2对象,若不加&则会调用拷贝构造函数拷贝出新的对象,
Person& PersonAddAge(Person &p)
{
this->age += p.age;
return *this;
}
int age;
};
void test(){
Person p1(10);
Person p2(10);
//p2.PersonAddAge(p1)返回值还是一个Person 叠加了p1年龄后的p2
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
}
4.3.3空指针访问成员函数
4.3.4 const修饰成员函数
4.4友元


4.5 运算符重载
注:未完待续
class Person
{
//成员函数重载运算符
Person operator+(Person &p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
int m_A;
int m_B;
}
//全局函数重载运算符
Person operator+(Person &p1, Person &p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
void test()
{
Person p1
p1.m_A = 10;
p1.m_B = 10;
Person p2
p2.m_A = 10;
p2.m_B = 10;
Person p3;
p3 = p1 + p2; // == p3 = p1.operator(p2); == p3 = operator+(p1,p2);
}
//总结: 对于内置的运算符不可以重载
// 不要滥用重载运算符
#include <iostream>
using namespace std;
class MYInteger
{
friend ostream& operator<<(ostream& cout, MYInteger myint);
public:
MYInteger(int num)
{
this->my_num = num;
}
MYInteger& operator++() //前置++
{
//先++后返回
my_num++;
return *this;
}
MYInteger operator++(int)
{
//先 记录结果,再返回
MYInteger temp = *this;
my_num++;
return temp;
}
private:
int my_num = 0;
};
ostream& operator<<(ostream& cout, MYInteger myint)
{
cout << "num = " << myint.my_num;
return cout;
}
void test01()
{
MYInteger MyInt(0);
cout << MyInt++ << endl;
cout << MyInt << endl;
}
int main()
{
test01();
return 0;
}
2.类和对象-继承
4.6.1 基本语法
4.6.2 继承方式
4.6.3 继承中的对象模型
子类在继承父类时,私有权限的也一样会被继承,只是被编译器隐藏了。所有子类的内存其实是自身再加上父类的总的内存大小
4.6.4 继承中的构造和析构顺序
4.6.5 继承同名成员处理方式
4.6.6 继承同名静态成员处理方式
4.6.7 多继承
语法: class 类名 :继承方式 父类名1, 继承方式 父类名2
在多继承下,访问父类中的成员需要明确加上作用域来区分。成员函数也同理
4.6.8 菱形继承,钻石继承
3.类和对象-多态
#include <iostream>
//多态
class Animal
{
public:
virtual void speak()
{
std::cout << "动物在说话" << std::endl;
}
};
class Cat: public Animal
{
public:
void speak()
{
std::cout << "小猫在喵喵" << std::endl;
}
};
class Dog: public Animal
{
public:
void speak()
{
std::cout << "小狗在汪汪" << std::endl;
}
};
//地址晚确定,在执行时根据传入类的对象才作相应的函数地址绑定
void DoSpeak(Animal &animal)
{
animal.speak();
}
void test01()
{
Dog dog;
Cat cat;
DoSpeak(dog);
DoSpeak(cat);
}
int main()
{
test01();
return 0;
}
4.7.1 多态案例-计算器类
4.7.3 纯虚函数
#include <iostream>
class AbstractDrink
{
public:
virtual void drinkwater() = 0;
virtual void chongpao() = 0;
virtual void daorubeizi() = 0;
virtual void Addres() = 0;
};
class DrinkCoffee : public AbstractDrink
{
public:
virtual void drinkwater()
{
std::cout << " 煮水中ing..." << std::endl;
}
virtual void chongpao()
{
std::cout << "冲泡咖啡!" << std::endl;
}
virtual void daorubeizi()
{
std::cout << "倒入被子!" << std::endl;
}
virtual void Addres()
{
std::cout << "加糖和牛奶" << std::endl;
}
};
class DrinkTea : public AbstractDrink
{
public:
virtual void drinkwater()
{
std::cout << " 煮水中ing..." << std::endl;
}
virtual void chongpao()
{
std::cout << "冲泡茶叶!" << std::endl;
}
virtual void daorubeizi()
{
std::cout << "倒入被子!" << std::endl;
}
virtual void Addres()
{
std::cout << "加柠檬!!" << std::endl;
}
};
void test01()
{
AbstractDrink *Drinking = new DrinkCoffee();
AbstractDrink *Drinking1 = new DrinkTea();
Drinking->drinkwater();
Drinking->chongpao();
Drinking->daorubeizi();
Drinking->Addres();
}
int main(void)
{
test01();
system("pause");
}
4.7.5 虚析构和纯虚析构