#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
// c语言中,不能放函数
struct _stu
{
int a;
int b[5];
};
// c++中,可以放函数
struct _stu1
{
int a;
int b[5];
void print_stu()
{
cout << a << endl;
}
};
struct student
{
// 学生的属性和数据
int age;
int id;
char name[256];
// 操作属性的叫做,方法或行为函数
void print()
{
cout << age << id << name << endl;
}
};
void test()
{
student obj;
obj.age = 10;
obj.id = 20;
strcpy(obj.name, "lucy");
obj.print();
}
int main()
{
test();
return 0;
}
c语言中表示事物时,将属性和行为分离,有可能行为调用出错
#include
// 表示人
struct Person
{
int age;
char name[128];
};
void Person_eat(struct Person *p)
{
printf("%s在吃饭\n", p->name);
}
// 表示dog
struct Dog
{
int age;
char name[128];
};
void Dog_eat(struct Dog *p)
{
printf("%s在吃粑粑\n", p->name);
}
void test()
{
struct Person p1;
p1.age = 20;
strcpy(p1.name, "bob");
Person_eat(&p1);
struct Dog d1;
d1.age = 7;
strcpy(d1.name, "旺财");
Dog_eat(&d1);
Dog_eat(&p1); // 人调用了狗的行为
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
// c++中对事物的封装,将属性和行为封装在一起
// 类将事物抽象成属性和行为,并且封装在一起
// 结构体中所有成员默认都是公有的,类中的所有成员默认是私有的,也可以修改成员的访问权限
// struct Person
class Person
{
public: // 公有的
// 类中的所有成员访问的权限都是私有的,private
// 属性
int age;
char name[128];
// 行为
void Person_eat()
{
printf("%s 吃饭\n", name);
}
};
struct Dog
{
// 属性
int age;
char name[128];
// 行为
void Dog_eat()
{
printf("%s 吃粑粑\n", name);
}
};
void test()
{
// 通过类 实例化出一个变量 这个变量叫对象
Person p1;
p1.age = 10;
strcpy(p1.name, "lucy");
p1.Person_eat();
Dog d1;
d1.age == 6;
strcpy(d1.name, "旺财");
d1.Dog_eat();
}
int main()
{
test();
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
class person
{
public: // 公有的,类内类外都可访问
int mTall; // 多高,可以让外人知道
protected: // 保护的,类外不可访问,类内是可以访问的,子类可以访问
int mMoney; // 有多少钱,只能儿子孙子知道
private: // 私有的,类外不可访问,类内是可以访问的,子类不可访问
int mAge; // 年龄,不想让外人知道
void show()
{
cout << mTall << " ";
cout << mMoney << " ";
cout << mAge << " ";
}
};
void test()
{
person p;
p.mTall = 180;
}
int main()
{
return 0;
}
设置成员变量为私有,优点:
例如
class AccessLevels
{
public:
// 对只读属性进行只读访问
int getReadOnly() { return readOnly; }
// 对只写属性进行只写访问
void setWriteOnly(int val) { writeOnly = val; }
// 对读写属性进行读写访问
void setReadWrite(int val) { readWrite = val; }
int getReadWrite() { return readWrite; }
private:
int readOnly; // 对外只读访问
int noAccess; // 外部不可访问
int readWrite; // 读写访问
int writeOnly; // 只写访问
};
#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
class Person
{
public:
void person_init(int age, char *name)
{
if (age >= 0 && age <= 100)
m_age = age;
strcpy(m_name, name);
}
void show_person()
{
cout << m_name << " " << m_age << endl;
}
int get_age()
{
return m_age;
}
void set_age(int age)
{
if (age >= 0 && age <= 100)
{
m_age = age;
}
}
char *get_name()
{
return m_name;
}
void set_name(char *name)
{
strcpy(m_name, name);
}
private:
int m_age;
char m_name[128];
};
void test()
{
Person p1;
p1.person_init(20, "lucy");
p1.show_person();
p1.set_age(30);
p1.set_name("bob");
p1.show_person();
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
class Cube
{
public:
void set_L(int l)
{
L = l;
}
void set_W(int w)
{
W = w;
}
void set_H(int h)
{
H = h;
}
int get_L()
{
return L;
}
int get_W()
{
return W;
}
int get_H()
{
return H;
}
// 求立方体的体积
int get_cube_V()
{
return L * W * H;
}
// 求立方体面积
int get_cube_S()
{
return 2 * W * L + 2 * W * H + 2 * L * H;
}
// 判断两个立方体是否相等
bool compare_cube(Cube &c1)
{
return c1.get_L() == L && c1.get_W() == W && c1.get_H() == H;
}
private:
int L;
int W;
int H;
};
bool comapre_cube(Cube &c1, Cube &c2)
{
return c1.get_L() == c2.get_L() && c1.get_W() == c2.get_W() && c1.get_H() == c2.get_H();
}
void test()
{
Cube c1;
c1.set_L(10);
c1.set_W(20);
c1.set_H(30);
cout << c1.get_cube_S() << endl;
cout << c1.get_cube_V() << endl;
Cube c2;
c2.set_L(20);
c2.set_W(20);
c2.set_H(30);
if (c1.compare_cube(c2))
{
cout << "立方体相等" << endl;
}
else
{
cout << "立方体不相等" << endl;
}
if (comapre_cube(c1, c2))
{
cout << "立方体相等" << endl;
}
else
{
cout << "立方体不相等" << endl;
}
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
using namespace std;
// 定义点类
class Point
{
public:
void setX(int x) { mX = x; }
void setY(int y) { mY = y; }
int getX() { return mX; }
int getY() { return mY; }
private:
int mX;
int mY;
};
// 定义圆类
class Circle
{
public:
void setP(int x, int y)
{
mP.setX(x);
mP.setY(y);
}
void setR(int r) { mR = r; }
Point &getP() { return mP; }
int getR() { return mR; }
// 判断点和圆的关系
void IsPointInCircle(Point &point)
{
// 距离平方
int distance = (point.getX() - mP.getX()) * (point.getX() - mP.getX()) + (point.getY() - mP.getY()) * (point.getY() - mP.getY());
// 半径平方
int radius = mR * mR;
if (distance < radius)
{
cout << "Point(" << point.getX() << "," << point.getY() << ")在圆内!" << endl;
}
else if (distance > radius)
{
cout << "Point(" << point.getX() << "," << point.getY() << ")在圆外!" << endl;
}
else
{
cout << "Point(" << point.getX() << "," << point.getY() << ")在圆上!" << endl;
}
}
private:
Point mP; // 圆心
int mR; // 半径
};
void test()
{
// 实例化圆对象
Circle circle;
circle.setP(20, 20);
circle.setR(5);
// 实例化点对象
Point point;
point.setX(25);
point.setY(20);
circle.IsPointInCircle(point);
}
int main()
{
test();
return 0;
}
构造函数:
析构函数:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
class Person
{
public:
// 构造函数1,函数名和类名一致,没有返回值,不能写void,可以有参数,可以发生函数重载
Person(int age, string name)
{
cout << "person的构造函数" << endl;
m_age = age;
m_name = name;
}
// 析构函数,函数名:类名前面加上~,没有返回值,不可以有参数,不能发生函数重载
~Person()
{
cout << "析构函数" << endl;
}
int m_age;
string m_name;
};
void test()
{
Person p1(10, "lucy"); // 构造函数是在实例化对象时会创建,就是在内存开辟空间时会被调用
// 销毁之前,自动调用析构函数
}
int main()
{
test();
return 0;
}
无参构造和有参构造,普通构造和拷贝构造
拷贝构造函数的写法:
类名(const 类名 &obj) {}
注意:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
class Person
{
public:
// 有参和无参构造
Person()
{
cout << "无参构造" << endl;
}
Person(int a, string n)
{
cout << "有参构造" << endl;
age = a;
name = n;
}
// 拷贝构造的调用时机:旧对象初始化新对象时
// 如过自定义了一个拷贝构造,那么系统不再提供默认的拷贝构造
Person(const Person &p) // Person p = p2
{
// 拷贝构造做了简单的值拷贝
age = p.age;
name = p.name;
cout << "拷贝构造" << endl;
}
int age;
string name;
};
void test()
{
// 如果人为提供了一个有参和有参构造,系统将不再提供默认的无参构造
Person p1; // 调用无参构造时,不能使用括号法
Person p2(10, "lucy");
Person p3(p2); // 调用拷贝构造
}
int main()
{
test();
return 0;
}
// explicit 关键字 // 修饰构造函数,作用是不能通过隐式法调用构造函数
explicit Person(const Person &p) {}
void test1()
{
cout << "匿名" << endl;
// 匿名对象,没有名字,生命周期在当前行
Person(10, "lucy"); // 调用了有参构造创建了一个匿名对象
Person(); // 调用了无参构造创建了一个匿名对象
Person p1(20, "heihei");
// Person (p1);//在定义时匿名对象不能使用括号法调用拷贝构造
cout << endl;
}
// 显示法调用构造函数
void test2()
{
cout << "显示法" << endl;
Person p1 = Person(10, "lucy"); // 显示法调用有参构造
Person p2 = Person(p1); // 显示法调用拷贝构造
Person p3 = Person(); // 显示法调用无参构造
cout << endl;
}
// 隐式法调用构造函数
void test3()
{
cout << "隐式法" << endl;
Person p1 = {10, "lucy"}; // 隐式法调用有参构造
// Person p2 = p1; // 隐式法调用拷贝构造
// Person p3 ;// 隐式法不能调用无参构造
}
总结一种情况:旧对象初始化新对象时
三种情况:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
class Person
{
public:
Person()
{
cout << "no param contructor!" << endl;
mAge = 10;
}
Person(int age)
{
cout << "param constructor!" << endl;
mAge = age;
}
Person(const Person &person)
{
cout << "copy constructor!" << endl;
mAge = person.mAge;
}
~Person()
{
cout << "destructor!" << endl;
}
public:
int mAge;
};
// 1 旧对象初始化新对象
void test1()
{
Person p(10);
Person p1(p); // 调用拷贝构造函数
Person p2 = Person(p); // 调用拷贝构造函数
Person p3 = p; // 相当于Person p2 = Person(p);调用拷贝构造函数
}
// 2 传递的参数是普通对象,函数参数也是普通对象,传递将会调用拷贝构造
void doBussiness(Person p) {} // Person p = p
void test2()
{
Person p(10);
doBussiness(p);
}
// 3 函数返回局部对象
Person MyBusiness()
{
Person p(10);
cout << "局部p:" << (int *)&p << endl;
return p;
}
void test3()
{
// vs release、qt下没有调用拷贝构造函数
// vs debug下调用一次拷贝构造函数
Person p = MyBusiness();
cout << "局部p:" << (int *)&p << endl;
}
int main()
{
cout << 1 << endl;
test1();
cout << 2 << endl;
test2();
cout << 3 << endl;
test3();
return 0;
}
默认情况下,c++编译器至少为我们写的类增加3个函数
如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数;
如果用户定义了普通构造(非拷贝),c++不在提供默认无参构造,但是会提供默认拷贝构造;
拷贝值,不拷贝地址(系统默认拷贝就是浅拷贝)
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
class Person
{
public:
Person(int age, char *str)
{
cout << "有参构造" << endl;
mAge = age;
name = (char *)malloc(strlen(str) + 1);
strcpy(name, str);
}
void show()
{
cout << name << " " << mAge << endl;
}
~Person()
{
if (name != NULL)
{
free(name);
name = NULL;
}
cout << "析构" << endl;
}
int mAge;
char *name;
};
void test()
{
Person p(10, "bob");
p.show();
Person p1(p); // 调用拷贝构造函数
p1.show();
}
int main()
{
test();
return 0;
}
拷贝值和地址
Person(const Person &p)
{
mAge = p.mAge;
name = (char *)malloc(strlen(p.name) + 1);
strcpy(name, p.name);
}
注意:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
class person
{
public:
// 先定义int m_a; int m_b; int m_c;然后再分别赋值
// person(int a, int b, int c)
// {
// m_a = a;
// m_b = b;
// m_c = c;
// }
// 先声明了int m_a; int m_b; int m_c;再根据声明的顺序进行定义初始化
// 调用构造函数是定义并初始化,顺序和声明的顺序一致
person(int a, int b, int c) : m_a(a), m_b(b), m_c(c) {} // int m_a = a;int m_c = c;int m_b = b;
void show()
{
cout << m_a << " " << m_b << " " << m_c << endl;
}
int m_a;
int m_c;
int m_b;
};
void test()
{
person p1(2, 3, 5);
p1.show();
}
int main()
{
test();
return 0;
}
类中有多个对象时,构造的顺序是先构造里面的对象,再构造外面的对象;
类中有多个对象时,析构时顺序是先析构外面的对象,在析构里面的对象;
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
using namespace std;
class Phone
{
public:
Phone(string name)
{
cout << "Phone的构造函数" << endl;
pho_name = name;
}
~Phone()
{
cout << "Phone的析构" << endl;
}
string pho_name;
};
class Game
{
public:
Game(string name)
{
cout << "Game的构造函数" << endl;
game_name = name;
}
~Game()
{
cout << "Game的析构" << endl;
}
string game_name;
};
class Person
{
public:
// Person(string per_name1, string pho_name, string g_name)
// {
// per_name = per_name1;
// phone.pho_name = pho_name;
// game.game_name = g_name;
// }
Person(string per_name, string pho_name, string g_name) : per_name(per_name), phone(pho_name), game(g_name)
{
cout << "person的构造函数" << endl;
}
void show()
{
cout << per_name << phone.pho_name << " 玩着 " << game.game_name << endl;
}
~Person()
{
cout << "Person的析构" << endl;
}
string per_name;
Game game;
Phone phone;
};
void test()
{
Person p1("bob", "诺基亚", "贪吃蛇");
p1.show();
}
int main()
{
test();
return 0;
}
c++提供了关键字explicit,禁止通过构造函数进行的隐式转换,声明为explicit的构造函数不能在隐式转换中使用;
explicit注意:explicit用于修饰构造函数,防止隐式转化,是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造)而言;
使用malloc和free函数去动态申请对象,和释放申请的对象,不会调用构造函数和析构函数
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
person()
{
cout << "person无参构造" << endl;
}
~person()
{
cout << "person析构" << endl;
}
int a;
};
void test()
{
cout << "malloc" << endl;
person *p = (person *)malloc(sizeof(person));
cout << "free" << endl;
free(p);
}
int main()
{
test();
return 0;
}
普通对象:
类型 *p = new 类型;
delete p;
数组:
类型 *p = new 类型[n];
delete[] p;
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
person()
{
cout << "无参构造" << endl;
}
~person()
{
cout << "析构" << endl;
}
int age;
};
void test1()
{
int *p = new int; // 申请一块内存,sizeof(int)大小,并且对这块空间进行初始化
cout << *p << endl;
*p = 100;
cout << *p << endl;
delete p; // 释放申请的空间
}
// 申请一个对象
void test2()
{
person *p = new person; // sizeof(person)
delete p;
}
// 申请一个数组
void test3()
{
// new一个数组时,返回的是该数组的首元素的地址
int *p = new int[10];
for (int i = 0; i < 10; i++)
{
p[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
cout << p[i] << " ";
}
cout << endl;
delete[] p;
}
int main()
{
return 0;
}
void test4()
{
// new时调用有参构造
person *p = new person(10);
delete p;
person *p1 = new person[10]; // 注意new对象的数组时不能调用有参构造 只能调用无参构造
delete[] p1;
}
void test5()
{
void *p = new person;
delete p; // p的类型是void *,所以不会调用析构函数
}
new单一对象时,使用delete释放单一的对象,new一个数组时,使用delete []释放这个数组
在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字static声明为静态的,称为静态成员;
不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享;
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
int a;
// 静态成员变量不能在类内初始化,类内只能声明,定义在全局,声明的作用只是限制静态成员变量作用域
static int b; // 静态成员变量,在编译阶段就分配内存,存在静态全局区
};
int person::b = 10; // 类中成员变量的定义
void test1()
{
person p1;
p1.b = 100;
cout << p1.b << endl;
}
void test2()
{
cout << person::b << endl; // 通过类的作用域访问类的静态成员函数
// cout << person::a << endl;
}
using namespace std;
int main()
{
test1();
cout << endl;
test2();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
int a;
// 静态成员变量不能再类内初始化,类内只能声明,定义在全局,声明的作用只是限制静态成员变量作用域
static int b; // 静态成员变量,在编译阶段就分配内存,存在静态全局区
void seta(int sa)
{
a = sa;
}
void show()
{
cout << a << " 普通成员函数 " << b << endl;
}
static void static_show() // 静态成员函数,可以访问静态成员变量,不能访问普通的成员变量
{
cout << " 静态成员函数 " << b << endl;
}
};
int person::b = 100;
void test()
{
person::static_show(); // 通过流类的作用域访问静态成员函数
person p1;
p1.seta(10);
p1.show();
p1.static_show(); // 通过对象访问静态成员函数
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
const int num = 10; // const修饰的全局变量保存在常量区,不可更改
class person
{
public:
int a;
// 静态成员变量不能再类内初始化,类内只能声明,定义在全局,声明的作用只是限制静态成员变量作用域
static int b; // 静态成员变量,在编译阶段就分配内存,存在静态全局区
const static int c = 1000; // const修饰的静态成员变量,是保存在常量区,不可修改(只读)在内存中只有一份
};
int person::b = 10; // 类中成员变量的定义
void test()
{
cout << person::c << endl;
person p1;
cout << p1.c << endl;
}
int main()
{
test();
return 0;
}
单例模式:一个类只能创建出一个对象
单例模式实现的步骤:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
// 1 将无参构造私有化
// 2 将拷贝构造私有化
// 3 定义一个静态的成员指针变量,指向new出来的一个唯一对象
// 4 将静态的成员指针变量私有化,提供获得唯一对象的地址的静态接口
class Feifei
{
public:
int age;
int yanzhi;
static Feifei *instance() // 4 将静态的成员指针变量私有化,提供获得唯一对象的地址的静态接口
{
return single;
}
private:
static Feifei *single; // 3 定义一个静态的成员指针变量,指向new出来的一个唯一对象
Feifei() // 1 无参构造私有化
{
}
Feifei(const Feifei &p) // 2 将拷贝构造私有化
{
}
};
Feifei *Feifei::single = new Feifei;
void test1()
{
// Feifei p1; // 不可直接访问无参构造
// Feifei p2;
}
void test2()
{
// Feifei::single->age = 100; // 不可直接访问单例
// Feifei::single->yanzhi = 100;
// Feifei p1(*Feifei::single); // 不可直接访问单例
// Feifei::single = NULL;
}
void test3()
{
Feifei *p = Feifei::instance();
p->age = 10;
p->yanzhi = 20;
Feifei *p1 = Feifei::instance();
cout << p1->age << " " << p1->yanzhi << endl;
}
int main()
{
test3();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
int a; // 普通的成员变量
static int b; // 静态成员不存在类实例化的对象中
void show() // 普通成员函数不存在类实例化的对象中
{
cout << a << " " << b << endl;
}
static void show1() // 静态成员函数 不存在类实例化的对象中
{
cout << b << endl;
}
};
int person::b = 1;
void test()
{
person p;
p.show();
// 空类的大小不是0,而是1
cout << sizeof(person) << endl;
}
int main()
{
test();
}
类的成员函数默认编译器都加上了一个this指针,这个this指针指向调用该成员函数的对象
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
person(int age, string name) // this
{
this->age = age;
this->name = name;
}
void show()
{
cout << age << " " << name << endl;
}
person person_add(person &p2) // this ------> p1
{
person p(this->age + p2.age, this->name + p2.name); //"helloworld"
return p;
}
int age;
string name;
};
person person_add(person &p1, person &p2)
{
person p(p1.age + p2.age, p1.name + p2.name); //"helloworld"
return p;
}
void test()
{
person p1(10, "hello");
person p2(20, "world");
// p3 = p1 + p2 // 30,"helloworld"
person p3 = person_add(p1, p2);
p3.show();
cout << endl;
person p4 = p1.person_add(p2);
p4.show();
}
int main()
{
test();
return 0;
}
// 常函数,不能通过this指针修改this指针指向的对象内容
// 常量指针常量
person person_addc(person &p2) const // const person * const this ------> p
{
// this->age = 200; // 不可修改
person p(this->age + p2.age, this->name + p2.name); //"helloworld"
return p;
}
类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部(作用域之外)访问;但是,有时候需要在类的外部访问类的私有成员,怎么办?
解决方法是使用友元函数,友元函数是一种特权函数,c++允许这个特权函数访问私有成员;
这一点从现实生活中也可以很好的理解:比如你的家,有客厅,有你的卧室,那么你的客厅是Public的,所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去,但是呢,你也可以允许你的闺蜜好基友进去;
如果想要让全局函数或一个类的成员函数访问另一个类私有成员,只需要声明友元即可
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Building
{
// 声明为友元
friend void print_Building(Building &b);
public:
Building(string hall, string bedroom)
{
this->bedroom = bedroom;
this->hall = hall;
}
string hall;
private:
string bedroom;
};
void print_Building(Building &b)
{
cout << b.hall << " " << b.bedroom << endl;
}
void test()
{
Building b1("我家", "卧室");
print_Building(b1);
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Building;
class Goodgay
{
public:
Goodgay(string hall, string bedroom);
void visit();
Building *b;
};
class Building
{
// friend void print_Building(Building &b);
// friend class Goodgay; // 一个类成为另一个类的友元
friend void Goodgay::visit(); // 类的成员函数成为另一类的友元
public:
Building(string hall, string bedroom)
{
this->bedroom = bedroom;
this->hall = hall;
}
string hall;
private:
string bedroom;
};
Goodgay::Goodgay(string hall, string bedroom)
{
b = new Building(hall, bedroom);
}
void Goodgay::visit()
{
cout << b->hall << " " << b->bedroom << endl;
}
void test()
{
Goodgay gd("我家", "卧室");
gd.visit();
}
int main()
{
test();
return 0;
}
运算符重载:就是给运算符赋予一个新的意义;
运算符只能运算内置的数据类型,对于自定义的数据类型,不能运算,所以我们可以重载运算符;
语法:
返回值类型 operator运算符(类引用) {}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
person(int age)
{
this->age = age;
}
person operator+(person &p2)
{
person p(this->age + p2.age);
return p;
}
int age;
};
// person operator+(person &p1, person &p2)
// {
// person p(p1.age + p2.age);
// return p;
// }
void test()
{
person p1(10);
person p2(20);
person p3 = p1 + p2; // 也可以operator+(p1,p2)或者p1.operator+(p2)
cout << p3.age << endl;
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
friend ostream &operator<<(ostream &cout, person &p);
public:
person(int age)
{
this->age = age;
}
private:
int age;
};
ostream &operator<<(ostream &cout, person &p)
{
cout << p.age; // 此处不能加endl
return cout; // 只有返回一个cout,函数中才能使用endl进行链式编程
}
void test()
{
person p1(10);
cout << p1 << endl;
operator<<(cout, p1);
}
int main()
{
test();
return 0;
}
C中几乎所有的运算符都可以重载,但重载运算符的使用相当受限制,特别是不能使用C中当前没有意义的运算符(例如用**求幂),不能改变运算符优先级,不能改变运算符的参数个数;
这样的限制有意义,否则,所有这些行为产生的运算符只会混淆原本的意义;
++a; //先自加,再使用
a++; //先使用,在自加
// 前置加加返回的是引用,后置加加返回的是对象
// 前置加加调用TYPE& operator++()函数,后置加加调用的是TYPE operator++(int)函数,也就是后置加加多了一个占位参数
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Myint
{
friend ostream &operator<<(ostream &cout, Myint &p);
public:
Myint(int num) // this
{
this->num = num;
}
Myint &operator++()
{
this->num = this->num + 1;
return *this;
}
Myint operator++(int)
{
Myint tmp = *this;
// 加加
this->num = this->num + 1;
return tmp;
}
int num;
};
ostream &operator<<(ostream &cout, Myint &p)
{
cout << p.num;
return cout;
}
void test()
{
Myint p1(10);
cout << p1 << endl;
++p1; // operator++(p1)或p1.operator++()
cout << ++p1 << endl;
p1++; // p1.operator++(int)
cout << p1 << endl;
}
int main()
{
test();
return 0;
}
我们new一个对象,经常忘记释放,所以我们可以使用智能指针来维护;
智能指针实质是一个局部对象,这个局部对象维护了new出来的对象的地址,在局部对象的析构函数中,会帮忙释放new出来的对象;
对于智能指针我们重载了->和*,可以让智能指针和普通指针一样使用;
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
person(int age)
{
this->age = age;
}
int age;
};
class Smartpointer
{
public:
Smartpointer(person *p1)
{
this->p = p1;
}
~Smartpointer()
{
delete p;
cout << "释放了p" << endl;
}
person *operator->()
{
return p;
}
person &operator*()
{
return *p;
}
person *p;
};
void test()
{
// 局部对象,在释放之前可以帮助释放p
// person *p = new person(10);
Smartpointer sp(new person(10));
// cout << p->age << endl;
cout << sp->age << endl; // sp->返回的是p
cout << (*sp).age << endl; // *sp返回的是*p
// 忘记释放p指向申请的对象,sp会自动释放p
}
int main()
{
test();
return 0;
}
编译器默认给每一个类加上了4个函数
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
person() {}
person(int age1, char *name1)
{
age = age1;
name = new char[strlen(name1) + 1];
strcpy(name, name1);
}
person &operator=(person &p1)
{
this->age = p1.age;
this->name = new char[strlen(p1.name) + 1];
strcpy(this->name, p1.name);
return *this;
}
~person()
{
delete[] name;
}
int age;
char *name;
};
void test()
{
person p1(10, "bob");
person p2;
p2 = p1; // p2.operator=(p1);
cout << p2.age << " " << p2.name << endl;
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class person
{
public:
person(int age, string name)
{
this->age = age;
this->name = name;
}
bool operator==(person &p2) // this->>>p1
{
return this->age == p2.age && this->name == p2.name;
}
bool operator!=(person &p2) // this->>>p1
{
return this->age != p2.age || this->name != p2.name;
}
int age;
string name;
};
void test()
{
person p1(10, "lucy");
person p2(20, "lucy");
if (p1 == p2) // p1.operator==(p2);
{
cout << "p1 == p2" << endl;
}
if (p1 != p2) // p1.operator!=(p2);
{
cout << "p1 != p2" << endl;
}
}
int main()
{
test();
return 0;
}
一个类中重载了()的类,那么整个类定义处的对象可以像函数一样使用,本质上是调用了operator()整个函数
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Myadd
{
public:
int add(int a, int b)
{
return a + b;
}
int operator()(int x, int y)
{
return x + y;
}
};
void test()
{
Myadd p;
cout << p.add(3, 4) << endl;
// p(),可以像函数一样调用的对象,称为函数对象
cout << p(3, 4) << endl; // p.operator()(3, 4);
cout << Myadd()(3, 4) << endl; // 等价于匿名对象Myadd().operator()(3, 4);
}
int main()
{
test();
return 0;
}
不要重载&&和||的原因是,无法在这两种情况下实现内置操作符的完整语义;
说得更具体一些,内置版本特殊之处在于:内置版本的&&和||首先计算左边的表达式,如果这完全能够决定结果,就无需计算右边的表达式了——而且能够保证不需要,我们都已经习惯这种方便的特性了;
我们说操作符重载其实是另一种形式的函数调用而已,而且函数调用总是在函数执行之前对所有参数进行求值;
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Complex
{
public:
Complex(int flag)
{
this->flag = flag;
}
Complex &operator+=(Complex &complex)
{
this->flag = this->flag + complex.flag;
return *this;
}
bool operator&&(Complex &complex)
{
return this->flag && complex.flag;
}
public:
int flag;
};
int main()
{
Complex complex1(0); // flag 0
Complex complex2(1); // flag 1
// 原来情况,应该从左往右运算,左边为假,则退出运算,结果为假
// 这边却是,先运算(complex1+complex2),导致,complex1的flag变为complex1+complex2的值
// 即complex1.a = 1 = complex2.a,1 && 1,结果为真
// complex1.operator&&(complex1.operator+=(complex2));
if (complex1 && (complex1 += complex2))
{
// complex1.operator+=(complex2)
cout << "真!" << endl;
}
else
{
cout << "假!" << endl;
}
// 原本应该输出假!但是输出了真!
return EXIT_SUCCESS;
}
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include
using namespace std;
class MyString
{
friend ostream &operator<<(ostream &out, MyString &str);
friend istream &operator>>(istream &in, MyString &str);
public:
MyString(const char *);
MyString(const MyString &);
~MyString();
// 重载[]
char &operator[](int index);
// 重载=
MyString &operator=(const char *str);
MyString &operator=(const MyString &str);
// 字符串拼接,重载+
MyString operator+(const char *str);
MyString operator+(const MyString &str);
// 字符串比较,重载==
bool operator==(const char *str);
bool operator==(const MyString &str);
private:
char *pString; // 指向堆区空间
int m_Size; // 字符串长度,不算'\0'
};
#include
#include "MyString.h"
// 左移运算符
ostream &operator<<(ostream &out, MyString &str)
{
out << str.pString;
return out;
}
// 右移运算符
istream &operator>>(istream &in, MyString &str)
{
// 先将原有的数据释放
if (str.pString != NULL)
{
delete[] str.pString;
str.pString = NULL;
}
char buf[1024]; // 开辟临时的字符数组,保存用户输入内容
in >> buf;
str.pString = new char[strlen(buf) + 1];
strcpy(str.pString, buf);
str.m_Size = strlen(buf);
return in;
}
// 构造函数
MyString::MyString(const char *str)
{
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_Size = strlen(str);
}
// 拷贝构造
MyString::MyString(const MyString &str)
{
this->pString = new char[strlen(str.pString) + 1];
strcpy(this->pString, str.pString);
this->m_Size = str.m_Size;
}
// 析构函数
MyString::~MyString()
{
if (this->pString != NULL)
{
delete[] this->pString;
this->pString = NULL;
}
}
char &MyString::operator[](int index)
{
return this->pString[index];
}
MyString &MyString::operator=(const char *str)
{
if (this->pString != NULL)
{
delete[] this->pString;
this->pString = NULL;
}
this->pString = new char[strlen(str) + 1];
strcpy(this->pString, str);
this->m_Size = strlen(str);
return *this;
}
MyString &MyString::operator=(const MyString &str)
{
if (this->pString != NULL)
{
delete[] this->pString;
this->pString = NULL;
}
this->pString = new char[strlen(str.pString) + 1];
strcpy(this->pString, str.pString);
this->m_Size = str.m_Size;
return *this;
}
MyString MyString::operator+(const char *str)
{
int newsize = this->m_Size + strlen(str) + 1;
char *temp = new char[newsize];
memset(temp, 0, newsize);
strcat(temp, this->pString);
strcat(temp, str);
MyString newstring(temp);
delete[] temp;
return newstring;
}
MyString MyString::operator+(const MyString &str)
{
int newsize = this->m_Size + str.m_Size + 1;
char *temp = new char[newsize];
memset(temp, 0, newsize);
strcat(temp, this->pString);
strcat(temp, str.pString);
MyString newstring(temp);
delete[] temp;
return newstring;
}
bool MyString::operator==(const char *str)
{
if (strcmp(this->pString, str) == 0 && strlen(str) == this->m_Size)
{
return true;
}
return false;
}
bool MyString::operator==(const MyString &str)
{
if (strcmp(this->pString, str.pString) == 0 && str.m_Size == this->m_Size)
{
return true;
}
return false;
}
#include
using namespace std;
void test()
{
MyString str("hello World");
cout << str << endl;
// cout << "请输入MyString类型字符串:" << endl;
// cin >> str;
// cout << "字符串为: " << str << endl;
// 测试[]
cout << "MyString的第一个字符为:" << str[0] << endl;
// 测试=
MyString str2 = "^_^";
MyString str3 = "";
str3 = "aaaa";
str3 = str2;
cout << "str2 = " << str2 << endl;
cout << "str3 = " << str3 << endl;
// 测试+
MyString str4 = "我爱";
MyString str5 = "北京";
MyString str6 = str4 + str5;
MyString str7 = str6 + "天安门";
cout << str7 << endl;
// 测试==
if (str6 == str7)
{
cout << "s6 与 s7相等" << endl;
}
else
{
cout << "s6 与 s7不相等" << endl;
}
}
int main()
{
test();
return 0;
}
一个类继承另一个类,这样类中可以少定义一些成员,能很好的解决代码重复的问题
派生类定义格式:
Class 派生类名 : 继承方式 基类名
{
// 派生类新增的数据成员和成员函数
}
三种继承方式:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
// 基类(父类)
class Animal
{
public:
int age;
void print()
{
cout << age << endl;
}
};
// 派生类(子类)
class Dog : public Animal
{
public:
int tail_len;
// 取消了重复的代码
// int age;
// void print()
// {
// cout << age << endl;
// }
};
void test()
{
Dog d;
}
int main()
{
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Base
{
public:
int a;
protected:
int b;
private:
int c;
};
// 公有继承,基类中是什么控制权限,继承到子类中也是什么控制权限
class A : public Base
{
// public:
// int a;
// protected:
// int b;
// private:
// int c;
public:
int d;
void show()
{
// 子类的成员函数可以访问父类的成员,但不能访问父类的私有成员
cout << a << b << c << d << endl;
}
};
class B : protected Base
{
// 保护继承,将父类中的公有的权限变成保护的,其他不变
// protected:
// int a;
// protected:
// int b;
// private:
// int c;
int d;
void show()
{
// 子类的成员函数可以访问父类的成员,但不能访问父类的私有成员
cout << a << b << d << endl;
}
};
class C : private Base
{
// 私有继承,会将所有的权限都变成私有的
// private:
// int a;
// private:
// int b;
// private:
// int c;
public:
int d;
void show()
{
cout << a << b << d << endl;
}
};
void test()
{
A p;
// p通过类外可以访问公有的权限
p.a = 1; // 公开的,可以直接更改
p.d = 2;
B p1;
// p1.a = 10; // 保护的,不能直接更改
// p1.d = 20;
C p2;
// p2.a = 100; // 私有的,不能直接更改
p2.d = 100;
}
int main()
{
test();
return 0;
}
在C++编译器的内部可以理解为结构体,子类是由父类成员叠加子类新成员而成
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Aclass
{
public:
int mA;
int mB;
};
class Bclass : public Aclass
{
public:
int mC;
};
class Cclass : public Bclass
{
public:
int mD;
};
void test()
{
cout << "A size:" << sizeof(Aclass) << endl; // 8
cout << "B size:" << sizeof(Bclass) << endl; // 12
cout << "C size:" << sizeof(Cclass) << endl; // 16
}
int main()
{
test();
return 0;
}
继承中的构造和析构
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Base
{
public:
Base(int age, string name)
{
this->age = age;
this->name = name;
cout << "Base的构造函数" << endl;
}
~Base()
{
cout << "Base的析构函数" << endl;
}
int age;
string name;
};
// 创建子类对象时,必须先构建父类,需要调用父类的构造函数
class Son : public Base
{
public:
Son(int id, int age, string name) : Base(age, name)
{
this->id = id;
cout << "Son的构造函数" << endl;
}
~Son()
{
cout << "Son的析构函数" << endl;
}
int id;
};
void test()
{
Son p(10, 18, "lucy");
}
int main()
{
test();
return 0;
}
如果子类和父类有同名的成员变量和成员函数,则继承时,父类的成员变量和成员函数会被隐藏
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Base
{
public:
Base(int a)
{
this->a = a;
}
void foo()
{
cout << "父类的foo函数" << endl;
}
int a;
};
class Son : public Base
{
public:
Son(int a1, int a2) : Base(a1), a(a2)
{
}
void foo()
{
cout << "子类的foo函数" << endl;
}
int a;
};
void test()
{
Son p(10, 20);
// 如果子类和父类有同名的成员变量,父类的成员变量会被隐藏,访问的是子类的成员变量
// 如果子类和父类有同名的成员函数,父类的成员函数会被隐藏,访问的是子类的成员函数
cout << p.a << endl;
p.foo();
}
int main()
{
test();
return 0;
}
继承时,子类不会继承父类的构造函数,析构函数和operator=函数
继承时,子类和父类有同名的静态成员函数或静态成员变量,父类中的静态成员函数或静态成员变量会被隐藏
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Base
{
public:
static int getNum() { return sNum; }
static int getNum(int param)
{
return sNum + param;
}
public:
static int sNum;
};
int Base::sNum = 10;
class Derived : public Base
{
public:
static int sNum; // 基类静态成员属性将被隐藏
#if 0
// 重定义一个函数,基类中重载的函数被隐藏
static int getNum(int param1, int param2)
{
return sNum + param1 + param2;
}
#else
// 改变基类函数的某个特征,返回值或者参数个数,将会隐藏基类重载的函数
static void getNum(int param1, int param2)
{
cout << sNum + param1 + param2 << endl;
}
#endif
};
int Derived::sNum = 20;
void test()
{
Derived p1;
// 如果子类和父类有同名的静态成员变量,父类中的静态成员变量会被隐藏
cout << p1.sNum << endl;
// 如果子类和父类有同名的静态成员函数,父类中的静态成员函数都会被隐藏
p1.getNum(1, 2);
}
int main()
{
test();
return 0;
}
即一个类继承了多个类
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class A
{
public:
int a;
};
class B
{
public:
int a;
};
class C : public A, public B
{
public:
int c;
};
void test()
{
C p;
p.A::a = 10;
p.B::a = 10;
// p.b = 20;
p.c = 30;
}
int main()
{
test();
return 0;
}
为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员
虚继承格式
class 子类 : virtual 继承方式 父类 {}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class animal
{
public:
int age;
};
class sheep : virtual public animal
{
public:
int id;
};
class camel : virtual public animal
{
public:
int camel_num;
};
class Shenshou : public sheep, public camel
{
public:
int a;
};
void test()
{
Shenshou p;
// p.sheep::age = 100;
p.age = 100;
}
int main()
{
return;
}
多态:一种接口,多种形态
静态多态:编译时,地址先绑定(静态联编)
动态多态:运行时,才确定需要调用的地址(动态联编)
发生多态的四个条件:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Animal
{
public:
virtual void speak() // 虚函数
{
cout << "动物在说话" << endl;
}
};
class Dog : public Animal
{
public:
// 重写虚函数,函数的返回值、参数、函数名一致
void speak()
{
cout << "狗在说话" << endl;
}
};
class Cat : public Animal
{
public:
void speak()
{
cout << "猫在说话" << endl;
}
};
// 如果两个类发生了继承,父类和子类编译器会自动转换,不需要人为转换
void do_work(Animal &obj)
{
obj.speak(); // 地址早绑定;函数前面加上virtual关键字,地址晚绑定
}
void test()
{
Animal p1;
do_work(p1);
Dog p2;
do_work(p2);
Cat p3;
do_work(p3);
}
int main()
{
test();
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
// 开发时,对源码的修改是关闭的,对扩展是开发的
class Mycalc
{
public:
int calc(int a, int b, string cmd)
{
if (cmd == "+")
{
return a + b;
}
else if (cmd == "-")
{
return a - b;
}
else if (cmd == "*")
{
return a * b;
}
}
};
void test1()
{
Mycalc p;
cout << p.calc(3, 4, "+") << endl;
cout << p.calc(3, 4, "-") << endl;
cout << p.calc(3, 4, "*") << endl;
}
// 多态实现计算器案例
class Calc
{
public:
virtual int mycalc(int a, int b)
{
return 0;
}
};
class Add : public Calc
{
public:
int mycalc(int a, int b)
{
return a + b;
}
};
class Sub : public Calc
{
public:
int mycalc(int a, int b)
{
return a - b;
}
};
class Mul : public Calc
{
public:
int mycalc(int a, int b)
{
return a * b;
}
};
int do_calc(int a, int b, Calc &obj)
{
return obj.mycalc(a, b);
}
void test2()
{
Add p;
cout << do_calc(2, 3, p) << endl;
Sub p1;
cout << do_calc(2, 3, p1) << endl;
Mul p2;
cout << do_calc(2, 3, p2) << endl;
}
int main()
{
test1();
cout << endl;
test2();
return 0;
}
C++中的动态绑定通过虚函数实现,而虚函数是通过一张虚函数表(virtualtable)实现的,拥有虚函数的类在实例化时会创建一张虚函数表;
这个表中记录了虚函数的入口地址,当派生类对虚函数进行重写时,虚函数表中相关虚函数的地址就会被替换,以保证动态绑定时能够根据对象的实际类型调用正确的函数;
纯虚函数:将虚函数等于0,实质是将虚函数表的函数入口地址设为NULL
抽象类:一个类中如果有纯虚函数,那么这个类就是一个抽象类,抽象类不能实例化对象
继承抽象类的子类也是一个抽象类,如果子类重写了虚函数,那么子类就不是抽象类
class Calc
{
public:
virtual int mycalc(int a, int b) = 0; // 虚函数等于0,纯虚函数
// {
// return 0;
// }
};
class Mod : public Calc
{
public:
// 子类继承了抽象类,那么子类也是一个抽象类
int mycalc(int a, int b) {} // 如果子类重写类虚函数,就不是抽象类
};
// 如果有纯虚函数的类,叫做抽象类,抽象类不能实例化对象
void test3()
{
// Calc p;
Mod p1;
}
注意:除了析构函数外,其他声明都是纯虚函数
作用:在调用基类的析构函数之前,会先调用子类的析构函
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
using namespace std;
class Animal
{
public:
virtual void speak() // 虚函数
{
cout << "动物在说话" << endl;
}
virtual ~Animal() // 虚析构作用:在调用基类的析构函数之前,会先调用子类的析构函数
{
cout << "Animal的析构" << endl;
}
};
class Dog : public Animal
{
public:
// 重写虚函数,函数的返回值、参数、函数名一致
void speak()
{
cout << "狗在说话" << endl;
}
~Dog()
{
cout << "狗的析构" << endl;
}
};
void do_work(Animal &obj)
{
obj.speak(); // 地址早绑定;函数前面加上virtual关键字,地址晚绑定
}
void test()
{
Animal *p = new Dog;
p->speak();
delete p;
}
int main()
{
test();
return 0;
}
纯虚析构函数等于0
class Animal
{
public:
virtual void speak() // 虚函数
{
cout << "动物在说话" << endl;
}
virtual ~Animal() = 0; // 纯虚析构
// {
// cout << "Animal的析构" << endl;
// }
};
重载:
重定义:
重写: