类与对象
/*
类与对象:
1.类是一种用户自定义的数据类型(函数,数据)
2.类是具有相同的属性和行为的对象的集合
3.类是对象的抽象,对象是类的具体
4.对象:通过使用类类型定义的变量
*/
定义类
/*
如何定义类?语法:
class 类名
{
默认是私有的
// 成员:1.数据 2.函数
//访问权限修饰关键字
public: // 公有的
// 成员:1.数据 2.函数
private: // 私有的(只允许当前类使用)
// 成员:1.数据 2.函数
protected: // 被保护的(只允许当前类或子类使用)
// 成员:1.数据 2.函数
}
*/
定义对象
/*
如何定义对象,语法:
类名 对象名;
e.g. Sheep xiYangYang
*/
访问成员
#include <iostream>
using namespace std;
class Sheep
{
public:
char name[32];
private:
int age;
public:
void eat()
{
cout << "让我们吃点草吧,咩!" << endl;
}
void speak(); // 函数声明
void setAge(int num)
{
age = num;
}
};
void Sheep::speak() // 在类的外面实现
{
cout << "我的年龄是" << age << "岁" << endl;
}
int main()
{
Sheep xiYY;
// xiYY.name = "喜羊羊"; 这个是错误写法
strcpy(xiYY.name, "喜羊羊");
// xiYY.age = 8; 错误写法,私有属性无法直接访问!!
xiYY.setAge(8); // 通过使用函数接口
xiYY.eat();
xiYY.speak();
cout << "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*" << endl;
// 通过指针实现:
Sheep* p;
p = &xiYY;
p->eat();
p->speak();
return 0;
}
/*
了解:class 和 struct
1.在 C语言里面,有 struct 没有 class
2.在 C语言里面,struct 里面“不能有”函数
3.在 C++ 里面,struct 里面“可以有”函数
4.在 C++ 里面,我们可以用 struct 定义结构体,也可以用它定义类
5.在 C++ 里面,用 class 定义的类,它的成员默认访问权限是私有的
而用 struct 定义类,它的成员默认访问权限是公有的,所以不太符合封装的思想
*/
String 类
/*
String 是 C++ 中的字符串
类似于C语言中的字符串数组
里面包括许多方法,使用时需要额外包含<string>
*/
#include <iostream>
#include <string> // 先预处理,下面才能使用 string
using namespace std;
int main()
{
char ch;
string str;
str = "abc123";
ch = str[2]; // 字母 c
ch = str.at(1); // 字母 b
str.length();
str.clear();
str.empty();
str == str;
return 0;
}
构造函数
/*
构造函数:
1.是一种特殊的成员函数
主要用来在创建对象时初始化对象,为对象的成员变量赋予初始值
2.构造函数名和类名相同
构造函数没有返回值类型,也没有返回值
3.构造函数可以重载,需要满足函数重载的条件
4.在创建一个新的对象的时候,会自动调用
如果一个类中没有显式地给出构造函数,系统会自动的给出一个构造函数
5.学会利用“成员初始化列表”,给成员初始化
*/
#include <iostream>
#include <string>
using namespace std;
class MyClass
{
public:
int id;
private:
int age;
public:
MyClass(); // 无参构造函数
MyClass(int n, int m); // 含参构造函数
};
MyClass::MyClass()
{
cout << "999" << endl;
}
MyClass::MyClass(int n, int m)
{
cout << id << endl;
}
int main()
{
MyClass obj_0; // obj_0 或者 obj_0() 都能激活无参构造函数
MyClass obj_1(5, 18); // 实例化对象的同时,激活有参构造函数
return 0;
}
#include <iostream>
#include <string>
using namespace std;
class MyClass
{
public:
const int id; // 注意!与C语言不同,在 C++ 中,const 是将被修饰的东西变成常量,即:不可改变
const int num;
public:
MyClass(int i) :id(i),num(5) // 成员初始化列表
{
}
};
int main()
{
MyClass obj(3);
cout << obj.id << endl;
cout << obj.num << endl;
return 0;
}
析构函数
/*
析构函数:
1.是一种特殊的函数,主要作用是在对象生命周期结束时进行清理,系统可以自动调用析构函数
2.析构函数可以主动通过对象调用,析构函数必须是公有属性下,在对象生命周期结束时会自动调用析构函数
3.不是因为调用了析构函数导致生命周期结束,而是因为生命周期结束时会自动调用析构函数首先
*/
#include <iostream>
#include <string>
using namespace std;
class MyClass
{
public:
MyClass();
~MyClass(); // 声明:析构函数
};
MyClass::MyClass()
{
}
MyClass::~MyClass() // 析构函数
{
cout << "析构" << endl;
}
int main()
{
MyClass obj; // 对象obj生命周期结束时,系统会自动调用析构函数
// 对于堆区里面的内存空间,因为需要自己申请,自己释放,所以没释放前,析构函数不会执行
MyClass* p = new MyClass;
delete p;
p = NULL;
return 0;
}
拷贝构造
/*
拷贝构造:
1.拷贝构造是一种特殊的构造函数,通过“拷贝构造”函数完成一个复制的过程。
2.特殊:参数是本类的对象的引用。
3.先是构造函数,才有可能是拷贝构造函数。没写的话,系统给,将成员一一对应赋值,也可以自定义。
*/
#include <iostream>
#include <string>
using namespace std;
class MyClass
{
MyClass(){}
~MyClass(){}
MyClass(const MyClass& obj){} // 拷贝构造函数的第一个参数是当前类的对象的引用
MyClass(const MyClass& obj, int n){}
MyClass(const MyClass& obj, int n,int m){}
};
int main()
{
return 0;
}
/*
调用时机:
1.使用一个对象给另一个对象进行初始化
2.使用一个对象构造另一个对象
3.函数的参数是类的对象
4.函数的返回值是类的对象
*/
#include <iostream>
#include <string>
using namespace std;
class Human
{
public:
string m_name;
int m_age;
public:
void showInfo();
// 下面的函数即使不写,也会默认存在
Human(); // 构造函数
Human(Human& obj); // 拷贝构造函数
~Human(); // 析构
};
void Human::showInfo()
{
cout << m_name << endl;
cout << m_age << endl;
}
Human::Human()
{
m_name = "KiKi[默认名]";
m_age = 0;
}
Human::Human(Human& obj)
{
m_name = obj.m_name;
m_age = obj.m_age;
cout << "执行了 Human(Human& obj) 函数" << endl;
}
Human::~Human()
{
}
void func_1(Human obj) // 函数的参数是类的对象
{
}
Human func_2()
{
Human obj;
return obj; // 函数的返回值是类的对象
}
int main()
{
Human obj_0;
obj_0.showInfo();
cout << "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n";
obj_0.m_name = "小明";
obj_0.m_age = 28;
obj_0.showInfo();
cout << "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n";
Human obj_1(obj_0); // 等价于 Human obj_1 = obj_0;
obj_1.showInfo();
cout << "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n";
// 3.函数的参数是类的对象
// 4.函数的返回值是类的对象
func_1(obj_1);
func_2();
return 0;
}
/*
浅拷贝与深拷贝:
1.浅拷贝:默认的都是浅拷贝
2.浅拷贝,无脑抄袭
3.深拷贝:需要自己根据实际情况实现
4.深拷贝:有点脑子
*/
this 指针
/*
this 指针:
1.this 指针是系统自动生成的,且隐藏的
2.this 指针不是对象的一部分,作用域在类内部
3.类的普通函数访问类的普通成员时,this 指针总是指向调用者对象
*/
#include <iostream>
using namespace std;
class MyClass
{
int num;
public:
void setNum(int num)
{
this->num = num;
}
int getNum()
{
(*this).getNum(); // this 指针的灵活运用
return num;
}
};
int main()
{
MyClass obj;
obj.setNum(8);
cout << obj.getNum() << endl;
return 0;
}
static 成员
/*
static(静态)关键字:
1.静态成员分为“静态数据成员”和“静态函数成员”
2.不管这个类实例化了多少个对象,这些对象都能够共享(类里面的)“静态区域”里面存的“静态数据”,即这些数据是共有财产
*/
#include <iostream>
using namespace std;
class MyClass
{
public:
static int num; // (静态数据成员)给初值的话,要在外面进行,如下
MyClass();
static void func(); // 静态函数成员
};
int MyClass::num = 0; // 如上
void MyClass::func()
{
cout << "func" << endl;
}
MyClass::MyClass()
{
num++; // 每创建一个对象,就记录一下
}
int main()
{
MyClass obj_1;
cout << obj_1.num << endl;
MyClass obj_2;
cout << obj_2.num << endl;
MyClass obj_3;
cout << obj_3.num << endl;
MyClass::num; // 因为数据 num 是在静态区域,所以还能通过类来访问
// 静态函数成员的访问
obj_3.func();
MyClass::func(); // 因为函数在静态区域,可以通过类来访问
(&obj_3)->func(); // 通过指针进行访问
return 0;
}
单例模式
/*
(入门级别)单例模式:创建一个只能实例化一个对象的类
*/
#include <iostream>
using namespace std;
class Human
{
public:
static Human* func();
private:
Human(); // 构造
Human(const Human& obj); // 拷贝构造
static Human* p; // 需要在外面给初始值,如下
};
Human* Human::p = NULL; // 给初始值操作
Human* Human::func()
{
if (p == NULL)
{
p = new Human; // 给更新值操作(只更新一次)
}
return p;
}
Human::Human() // 声明了构造,就一定要写,即使函数里面什么都不做
{}
Human::Human(const Human& obj)
{}
int main()
{
Human* p1 = Human::func();
cout << hex << p1 << endl; // hex 以16进制输出
Human* p2 = Human::func();
cout << hex << p2 << endl;
Human* p3 = Human::func();
cout << hex << p3 << endl;
return 0;
}
const 成员
/*
const 关键字:
1.常量成员分为"常量数据成员"和"常量函数成员"
2.使用了 const,就不能修改内部数据
3.补充知识:常对象
*/
#include <iostream>
using namespace std;
class Human
{
public:
const int num; // 常量数据成员,初始化必须用"成员初始化列表"
Human(); // 无参构造
Human(int a); // 含参构造
void func()const; // 常量函数成员
static int m; // 静态(数据)成员
};
int Human::m = 0;
Human::Human() :num(5) // 成员初始化列表
{}
Human::Human(int a) : num(a) // 成员初始化列表
{}
void Human::func()const
{
cout << num << endl; // 在常量函数成员中,普通的数据成员,如 num 等,只能读,不能改
m = m + 5; // 在常量函数成员中,静态数据成员,如 m 等,可以修改
this->m = 99;
}
int main()
{
// 下面两种都是“常对象”-----整个对象里面的数据都不能改,静态数据除外
const Human obj;
Human const obj;
return 0;
}
友元关系
/*
友元关系:
1.类的特性之一是封装,友元是打破封装的手段 ( 不要滥用!!)
2.友元分为"友元函数"和"友元类"
3.友元类,可以创建友元对象
*/
// 友元函数
#include <iostream>
using namespace std;
class A
{
private:
int num;
friend void func(A obj); // 友元关键字 friend
public:
A(){ num = 0; } // num 初始化
};
void func(A obj) // 普通函数
{
obj.num = 6; // 本来是不能访问类中私有的 num 的,但是因为上面加了 friend
}
int main()
{
A obj_1;
func(obj_1);
return 0;
}
// 友元类
#include <iostream>
using namespace std;
class A
{
private:
int num;
friend class B; // 把 B 当作朋友(单方面)
};
class B
{
public:
void func(A obj)
{
obj.num; // 本来是不能访问 A 中的私有成员 num,但是因为 A 中加上了 friend,从此 B 中都能使用
}
};
int main()
{
return 0;
}
继承与派生
/*
1.继承的概念:
A =====> B
父类 子类
基类 派生类
2.继承和派生:
继承和派生描述的是同一个事情,只是看待事情的角度不一样,说法不一样罢了。
子类继承父类
父类派生子类
3.继承分类
1.单继承
2.多继承
*/
继承方式
/*
单继承和多继承语法:
class 父类名
{
};
1.单继承
class 子类名 : 继承权限 父类名
{
};
2.多继承
class 子类名 : 继承权限1 父类名1,继承权限2 父类名2 ......
{
};
3.继承权限
1.公有继承 public
2.私有继承 private
3.保护继承 protected
*/
公有继承 public | 私有继承 private | 保护继承 protected | |
---|---|---|---|
public | public | private | protected |
private | 不可访问 | 不可访问 | |
protected | protected | private | protected |
父子关系
/*
子类在继承父类的过程中,如果子类的number和父类的number冲突,那么默认会使用子类的number,
而父类的numbe r想要使用的话需要强调一下,它是来自父类的number
e.g.
// 下面是子类的对象obj
obj.number // 默认是子类中的number
obj.FatherClass::number // 来自父亲的number
*/
菱形继承
#include <iostream>
using namespace std;
class A
{
public:
int n;
};
class A_1 : virtual public A // 虚继承
{
public:
int a1;
};
class A_2 : virtual public A // 虚继承
{
public:
int a2;
};
class AA : public A_1, public A_2
{
public:
int aa;
};
int main()
{
AA obj;
obj.aa;
obj.a1;
obj.a2;
obj.A_1::n;
obj.A_2::n;
obj.n; // 虚继承发挥的作用
return 0;
}
多态
/*
1.什么是多态?
1.“子类对象的地址”可以赋值给“父类指针”
对于通过“父类指针”调用“父类和子类中都有的同名、同参数表的【虚函数】”的语句
编译时并不确定要执行的是父类的【虚函数】,还是子类的【虚函数】
当程序运行到该语句时
如果“父类指针”指向的是一个“父类对象”,则父类的【虚函数】被调用
如果“父类指针”指向的是一个“子类对象”,则子类的【虚函数】被调用
我们称这种机制,叫作“多态(polymorphism)”
2.包含【虚函数】的类称为“多态类”
“多态”可以简单地理解为,同一条函数调用语句能调用不同的函数
或者说,对不同对象发送同一消息,使得不同对象有各自不同的行为
2.什么是虚函数?
所谓“虚函数”,就是在声明时前面加了 virtual 关键字的成员函数
注意!virtual 关键字只在类定义中的成员函数声明处使用,不能在类外部写成员函数体时使用
同时,要注意,静态成员函数不能是【虚函数】。
3.补充:
“多态”在面向对象的程序设计语言中非常重要,以至于有类和对象的概念
不支持多态的语言,只能被称作“基于对象的程序设计语言”而不能被称为“面向对象的程序设计语言”
比如:Visual Basic 就是前者
---内容借鉴于"C语言中文网" [Go]
*/
纯虚函数
#include <iostream>
using namespace std;
class A // 含有“纯虚函数”的类叫做抽象类
{
public:
virtual ~A() = 0; // 析构函数实现"纯虚函数"时,还是要在外面写一下函数体的
virtual void Print() = 0; // 纯虚函数格式:"virtual" + "=0" + "没有函数体"
};
A::~A()
{
}
int main()
{
// 抽象类不能用来定义对象(即:实例化对象),但是可以定义指针
// 有什么用?可以为子类服务
return 0;
}
数据抽象
什么是数据抽象
- 数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节
- 数据抽象是一种依赖于接口和实现分离的编程(设计)技术
数据抽象的好处
- 类的内部受到保护,不会因无意的用户级错误导致对象状态受损
- 类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告
#include <iostream>
using namespace std;
class Adder{
public:
// 构造函数
Adder(int i = 0)
{
total = i;
}
// 对外的接口
void addNum(int number)
{
total += number;
}
// 对外的接口
int getTotal()
{
return total;
};
private:
// 对外隐藏的数据
int total;
};
int main( )
{
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
通过抽象类来实现接口
数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
C++ 通过创建类来支持封装和数据隐藏(public、protected、private)
#include <iostream>
using namespace std;
// 基类
class Shape
{
public:
// 提供接口框架的纯虚函数
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 派生类
class Rectangle: public Shape
{
public:
int getArea()
{
return (width * height);
}
};
class Triangle: public Shape
{
public:
int getArea()
{
return (width * height)/2;
}
};
int main(void)
{
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
// 输出对象的面积
cout << "Total Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
// 输出对象的面积
cout << "Total Triangle area: " << Tri.getArea() << endl;
return 0;
}