目录
C++丨8丨类和对象的特性
面向对象程序设计的4个主要特点:抽象、封装、继承、多态性。
类的成员访问权限有3种:
public(公用)、protected(保护)、private(私有)。没有static(静态)!
在类外定义成员函数
在函数名前面加上【::】作用域限定符 即可!(不加的话就是全局函数啦
class Student {
public:
void F(); // 函数原型声明
private:
string name;
};
void Student::F() {
}
PS:以上形式是一种书写类的好习惯! 要学习!(先写public函数成员,然后写private数据成员,然后在类外实现函数。)
内置成员函数 inline
如果在类体中定义的成员函数中不包括循环等控制结构,C++系统自动地对它们作为内置(inline)函数来处理。
对类内定义的成员函数,可以省略inline,因为这些成员函数已被隐含地指定为内置函数。(好像根本没有人会写的样子...
class Student {
public:
void F() {}
// 等价于 inline void F() {}
private:
string name;
};
意思是说类外定义的时候不会被默认为内置函数哦!想要就自己写。
成员函数的存储方式
同一个类的成员函数只存一份!(即一堆对象跑去类里用同一个函数。)不过数据是单独存放在各个对象里的!对象的存储空间里只有数据,没有函数!
对象成员的引用
访问对象中成员的3种方式:(只能访问public公有成员!)
(1)通过对象名和成员运算符访问对象中的成员
对象名.成员名
Time t;
t.hour;
t.F();
(2)通过指向对象的指针访问对象中的成员
Time t;
Time* p = &t; // p指向对象t
cout << p->hour;
(3)通过对象的引用访问对象中的成员
Time t1; // 定义对象t1
Time& t2 = t1; // 定义Time类引用t2,并使之初始化为t1
cout << t2.hour;
C++丨9丨怎样使用类和对象
:你怎么好像什么都不会的样子。
构造函数(construction)
class Box {
Box() {} // 构造函数
};
(1)用来处理对象的初始化。(因为你不可以在声明数据成员的时候给它们赋值。)
(2)构造函数是一种特殊的成员函数,它与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。(只能执行一次。系统自动调用,不可以被用户调用)
(3)构造函数的功能是由用户定义的,用户根据初始化的要求设计函数体和函数参数,可以是一个,也可以是多个。
(4)构造函数可以重载!(函数名相同,不会返回任何类型,也不可以是void类型,参数类型/个数可不同)。
(5)构造函数的名字必须与类名相同!!
构造函数分为几种:
class Date
{
int year;
int month;
int day;
(1)系统默认的构造函数(默认构造函数)
// 系统默认的构造函数
Date() { } // 即 Date() = default
// 一旦你写了一个别的构造函数,系统就不生成这个了!
(2)不带参数的构造函数
// 不带参数的构造函数
Date() {
year = 1999;
month = 9;
day = 9;
}
(3)带参数的构造函数(带缺省值的构造函数)
// 带参数的构造函数
Date(int y, int m) {
year = y;
month = m;
day = 9;
}
// 带参数的构造函数
Date(int y, int m, int d) {
year = y;
month = m;
day = d;
}
// 可以对其中某一些数据指定默认值,但不能全部都有默认值!!不然就是类型(4)
// 这种方式下,有2或3个参数都可以执行。
Date(int y, int m, int d = 9) {
year = y;
month = m;
day = d;
}
PS:这里构造函数一多很容易不小心出现歧义。要具体情况具体分析!
大概思路就是,假设你在构造对象的时候输入了n个参数,其中能运行n个参数的情况的构造函数(一个一个判断下去)有几个。如果只有一个就没有歧义!
PS:在声明构造函数的时候指定了默认值的话,定义(实现)函数的时候就不要再写一遍了!会报错。可参见《Ruru的报错日常》里的 [Error]005 TT
Box(int h = 10, int w = 10); // 声明
Box::Box(int h, int w) {...}; // 定义。不要再写 =10 啦!
(4)使用默认参数的构造函数⭐
// 使用默认参数的构造函数
// 输入0、1、2、3个参数时都可以执行!
Date(int y = 1999, int m = 9, int d = 9) {
year = y;
month = m;
day = d;
}
⭐PS:有了这种全部是默认参数的构造函数后,就不能再定义重载构造函数了!!
因为此时你即使只写 Date(2000); 也是可·以·运·行的!效果为将y由1999替换为2000。假设你还定义了一个只有一个参数的构造函数,此时就会有·歧·义!!所以!
⭐反正就是无论你输入几个参数都可以执行,所以有其它构造函数时必然会出现歧义。(啊!受不了我的表达能力了!
⭐PS:以下两种都属于默认构造函数!不能同时存在!
// 1. 没有参数
Date();
// 2. 所有参数均有默认值
Date(int y = 1999, int m = 9, int d = 9);
默认构造函数:总的来说就是调用构造函数时,不必给出实参的构造函数!
用参数初始化表对数据成员进行初始化
这种方法不在函数体内对数据成员初始化,而是在函数首部实现。
// 用参数初始化表对数据成员初始化
Date(int y, int m, int d) :year(y), month(m), day(d) {
// 构造函数体(可写可不写)
}
Date date(1999, 9, 9);
};
使用无参构造函数
Box box1; (√)
Box box2(); (x)
另:构造函数不可以被显式调用,所以请不要出现 调用 Box() 这种神奇的形式!
析构函数(destructor)
class Box {
Box() {} // 构造函数
~Box() {} // 析构函数
};
(1)析构函数的名字也是固定的:~类名
( ~ 位取反运算符。So:析构函数是与构造函数作用相反的函数。(好好嗑!
(2)当对象的生命期结束时,会自动执行析构函数。
(3)我们的口号是!——清除对象,释放内存。(析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作)
(4)析构函数没有返回值、函数类型、函数参数。不可以被重载。一个类可以有多个构造函数,但是只能有一个析构函数。(等等,突然不那么好嗑了....
(5)它可以被用来执行 用户希望在最后一次使用对象之后所执行的任何操作。例如输出相关的信息。(嗯??什么遗言(。
调用构造函数和析构函数的顺序
省流:像洋葱!(or先穿袜子再穿鞋子,然后脱鞋子脱袜子.... or搭积木 or something...
总之就是先构造的后析构。(这里纯粹是代码执行顺序。不关继承or套娃的事哦。)
对象数组
Box box[3] = {
Box(1, 2),
Box(3, 4),
Box(5, 6);
};
对象指针
(1)指向对象的指针
和结构体指针类似!传送门:C++丨7丨用户自定义数据类型_巧克力Pie的博客-CSDN博客
(2)指向对象数据成员的指针
p1 = &t1.hour; // p1指向t1.hour
cout << *p1; // 输出t1.hour的值
(3)指向对象成员函数的指针
重温普通函数的指针变量定义方法:
void(*p)(); // p是指向void型函数的指针变量
p = fun; // 将fun函数的入口地址赋给指针变量p。p指向fun
(*p)(); // 调用fun函数
定义一个指向对象成员函数的指针变量的方法:
p1 = t1.get_time; (x) 报错!!
void (Time::* p2)(); // 定义p2为指向Time类中公有成员函数的指针变量
// (Time::* p2)两边的()不可以省略!否则p2会先和后面的()结合
void Time::* p3(); 相当于:
void(Time::* (p3())); // 返回值为void型指针的函数
p2 = &Time::get_time; (√)
this指针///
......
共同数据的保护
常对象 const
作用:数据共享+防止被修改
(1)常对象必须要有初值。
Time const t1( 12, 0, 0); // 定义t1为常对象
const Time t1( 12, 0, 0); // 定义t1为常对象
两种写法都可以!
(2)初始化之后不能再改变。(数据共享+防止被修改)
(3)如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数(内置保护机制:不可以修改数据的值),而不能调用该对象的普通成员函数(除了由系统自动调用的隐式的构造函数和析构函数)。
将函数成名为const的方法:
void get_time() const; // 将函数声明为const
mutable///
......
常对象成员///
对象的赋值和复制
对象的赋值///
对象的复制
(1)操作系统生成的:浅复制
类名 对象2(对象1);
Box box2(box1);
// The copy constructor definition
Box::Box(const Box& b) {
height = b.height;
width = b.width;
length = b.length;
}
(2)自定义复制构造函数:深复制。
拷贝构造函数(copy constructor)
(1)类在我们没有定义拷贝构造函数的时候,会默认定义默认拷贝构造函数,也就是说可以直接用同类型的类间可以相互赋值、初始化。
(2)拷贝分为:浅拷贝、深拷贝。
静态成员 static
静态数据成员
(1)共有数据 / 数据共享!如果希望各对象中的数据成员的值是一样的,可以把它定义为静态数据成员!
(2)静态数据成员是类的成员,而不是对象的成员。
(3)它的数据存在单独的地方。不和对象成员一起。
(4)静态数据成员可以初始化,但只能在类外进行初始化!!
数据类型 类名::静态数据成员名 = 初值;
// PS:在类内声明时加static即可。初始化的时候不要加!
class Box {
static int height;
};
int Box::height = 10;
(5)不能用参数初始化表对静态数据成员初始化。
(6)在类外可以通过对象名访问公共的静态数据成员,也可以通过类名引用静态数据成员。
Box box;
box.height; (√) 通过对象名访问公共的静态数据成员
Box::height; (√) 通过类名引用静态数据成员
静态成员函数///
(1)无this指针!
(2)只能访问静态数据成员。(不然0个对象的时候其它值都没有数据)
友元 friend ///
友元函数 ///
书写:在类外定义直接写(不用加friend),在类内声明要加上friend前缀。
⭐并且!类外要再声明一次不带friend的。(而且要在类声明之后。)
(1)将普通函数声明为友元函数 ///
不用写xx::
(2)友元成员函数 ///
要写xx::
友元类 ///
类模板 template ///
模板是一组类(或函数)的抽象。
我们来看一下对比!
普通类和它的函数:
class MyClass
{
int data;
public:
MyClass(); // 构造函数
}
// 构造函数
MyClass::MyClass()
{
//
}
写成类模板和它的函数:
类模板:前面要加 template <class Type> ,需要用到 Type 的地方就写 Type。
template <class Type> // 一般也会写成T
class MyClass
{
Type data; // Type类型的数据
public:
MyClass(); // 构造函数
}
函数:前面要加 template <class Type> ,类名后面要加 <Type>。
// 构造函数
template <class Type>
MyClass<Type>::MyClass()
{
//
}
在主函数中使用:
int main()
{
MyClass<student> stu(20);
}
参考:C++编程——类模板_Stephen_Tao的博客-CSDN博客
参考: