名字空间
namespace 的基础与扩展
#include <iostream>
using namespace std;namespace ns
{int a = 3;float f(int x){return x / 2;}namespace ns_ns // 可以进行嵌套{int a = 6;float f(int x){return x / 3;} }
}namespace ns
{int b = 5; // 直接扩充成员
}int main()
{cout << ns::a << endl; // 使用自创的名字空间,必须要告知来源cout << ns::f(6) << endl;cout << ns::ns_ns::a << endl; // 同样的,嵌套使用必须说明来源cout << ns::ns_ns::f(6) << endl;cout << ns::b << endl;return 0;
}若两个命名空间出现相同名字,则只需在调用时明确告知来源即可,
如:假设存已命名空间 name 内部包含 a,则cout << ns::a << endl;
cout << name::a << endl;
面向对象三大特征
封装
把对象的属性和功能结合成一个独立的系统单位。(尽可能隐藏内部细节,对外形成屏障)
如:人使用电脑,只需要知道键位功能,不需要知道键位是如何绑定函数调用的。
继承
若类的对象A拥有另一个类B的全部属性与服务,称作类A对类B的继承。
如:机动车共有特性发动机,而小汽车、摩托车等都继承了该共有属性。
多态
指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或行为在一般类及其各个 特殊类中具有不同的语义
类的封装
类与对象的定义
相似于 c 语言的结构体,但存在以下区别:
1、含有作用域限定符
2、含有函数,也就是类的自身行为(类方法)
#include <iostream>using namespace std;class A
{public: // 若不添加,默认是 privateint a;private:// 类方法:类中的函数void show(){cout << a << endl;}
};int main()
{A a1;a1.a = 10;a1.show(); // 通过类产生的对象来调用方法return 0;
}
类成员权限限定符
公有成员(public
):任何地方都可以访问
保护成员(protected
);只能在本类或其他后代类中访问
私有成员(private
);只能在本类中访问
一般原则:1、类数据属性,设定为私有;2、类方法,设定为公有;#include <iostream>
using namespace std;class Kitty
{public:void eat() { cout << "吃饭" << endl; }void sleep() { cout << "睡觉" << endl; }void actCute() { cout << "卖萌" << endl; }void f() { cout << "父类 f" << endl; }
};class A : public Kitty
{public:void f() { cout << "子类 f" << endl; }
};int main()
{Kitty myCat;myCat.eat();myCat.sleep();myCat.actCute();myCat.f(); // Kitty::f();A a;a. f (); // A::f(); 在本来对象可以简写调用a. Kitty::f (); // 此时在子类调用从父类继承下来的方法,只需要明确说明即可return 0;
}注意:被 protected 和 private 修饰,不可以被类直接调用
类的构造函数
概念:类的特殊成员,专门用于初始化类的各项数据(分配内存、变量初始化)
特点:
1、任何类都必然有至少一个构造函数,可以重载
2、若类显式定义构造函数,则默认无参构造函数会被取消(调用系统默认构造函数,这样就无法得知构造函数干啥。所以不要使用系统的默认构造函数);若无显示定义构造函数,则系统会自动添加一个隐式构造函数
3、每当分配一个类对象时,构造函数会被自动调用
4、构造函数无返回值类型(不是void
),且必须与类名相同
5、构造函数不被设置为静态成员函数,构造函数的函数首部之前或之后不能用const
修饰
#include <iostream>
using namespace std;class Node
{public:// 特别注意:下面这种情况,两种构造函数都写了,那么就看对象创建时是否带参数// 若默认无参构造函数未显式写出,而类对象创建时不带参数,那么就会报错,因为没有匹配的构造函数Node() { cout << "我是默认构造函数" << endl; }Node(int a) { cout << "我是显式的构造函数,我的编号是:" << a << endl; }Node(int a, float b); // 函数重载private:string name; // 不可以直接赋值int high;
};int main()
{// 动态分配会在堆区,而不会出现在栈区,这就是需要动态分配的原因Node node;// Node *node = new Node; //普遍应用的一种方式,通过 new 可以调用构造函数,这是 malloc 不具备的(本质就是 new 除了会分配内存,还会调用构造函数进行变量初始化,而 malloc 只能分配内存,不能初始化)return 0;
}小小认知:( 变量 )初始化:本质是为变量申请开辟内存空间,并没有直观的体现赋值:将数值存储在已初始化变量上,就是存储在内存空间中所以,构造函数的变量初始化是为了更加细化的给变量分配内存空间
this 指针
#include <iostream>
using namespace std;class Node
{public:Node(string name) { this->name = name } // 用来区分哪个是传参,哪个是本身,当然可以不用相同的名字// 若要返回本身Node f() { return *this; }private:string name; // 不可以直接赋值int high;
};int main()
{Node node("老六");return 0;
}
类的析构函数
概念:类的特殊成员,专门用于处理类对象被释放时的收尾工作
特点:
1、任何类有且仅有一个析构函数,不可以重载
2、若类显式定义析构函数,则默认无参构造函数会被取消;若无显示定义析构函数,则系统会自动添加一个隐式析构函数
3、每当释放一个类对象时,析构函数会被自动调用
4、构造函数无返回值类型(不是void
),也没有参数,且必须与类名相同,且在前面多一个波浪号
5、如果类对象的作用域相同,那么销毁时析构函数的执行顺序与构造函数相反
#include <iostream>using namespace std;class Node
{public:Node() { cout << "我是构造函数" << endl; }~Node() { cout << "我是析构函数" << endl; }
};int main()
{Node *node = new Node;delete node;return 0;
}
拷贝构造函数
一种特殊的构造函数,其作用是在构造对象时,复制其他对象的所有成员。
1、拷贝构造函数,要求形参中有一个当前类的类对象
2、若没有就使用默认,若存在则使用存在的,特别需要注意的是:显式的拷贝构造函数基本不会影响已存在的默认拷贝构造函数,除非提供了单参构造函数,且参数类型是类类型的引用
3、拷贝分为浅拷贝和深拷贝,浅拷贝只拷贝值,深拷贝还会拷贝内存(这样也就不会导致重复释放同一个空间)
4、若显示声明和定义拷贝构造函数后,类中的的所有成员变量需要手动拷贝
5、当使用一个老对象去构造一个新对象时会调用拷贝构造函数,如果没有显示声明和定义拷贝构造函数时,类中的非指针类成员的值可以拷贝。默认拷贝构造函数负责执行了值的拷贝
三种应用场景:
(1)用一个老对象构造一个新对象,或者新对象赋给老对象
(2)当函数的形参是一个非引用的类对象时,形参对实参的传递时
(3)函数返回一个非引用的类对象时
#include <iostream>using namespace std;class Node
{public:Node(int a) { cout << a << endl; }Node(const Node &p) { cout << "调用拷贝构造" << endl; }
};int main()
{Node *node = new Node(1);Node *node1 = node; // 会自动调用拷贝构造函数delete node;delete node1; // 这会导致 double free ,所以需要进行深拷贝(将内存空间也要拷贝一份)return 0;
}
-------------------------------------------------- 深拷贝与浅拷贝 --------------------------------------------------
系统默认拷贝构造是浅拷贝,也就是相当于取别名,复制多个指针,但还是指向同一块内存空间。这样就会造成一个结果:重复释放了某一块内存空间,所以一般需要深拷贝以下重点讲解怎么进行深拷贝:#include <iostream>
#include <string>
using namespace std;class A
{public:A() { p = new char[100]; }~A() { delete p; }// 将拷贝构造函数进行重写,另外需要开辟空间,改造成深拷贝A(const A &r){if(r.p != nullptr){p = new char[100];memcpy(p, r.p, 100);}elsep = nullptr;}private:char *p;int a;
}
空类中默认的类方法
无参构造函数 析构函数 拷贝构造函数 赋值运算符函数
const 成员
const
基本语义就是将其修饰的符号作只读化处理(不可修改)。
#include <iostream>
using namespace std;class person
{public:person();~person();private:const unsigned int ID; // const 修饰类成员变量public:void studentInfo() const; // const 修饰类方法
};// 方式一:类构造函数初始化列表中初始化
person::person(unsigned int id): ID(id)
{}// 方式二:类定义语句中初始化
class person
{private:const unsigned int ID = 12345;
};注意:1、方式一可以通过构造函数传递不同的参数来给 ID 不同初始值 ---- 适用场景:学生的学号2、方式二则是所有对象被构造出来的 ID 都是一样的且不可修改 ---- 适用场景:学生的学校
-------------------------------------------------- 实际代码展示 ---------------------------------------------------
#include <iostream>
using namespace std;class Student
{private:const string schoolName = "粤嵌"; // 所有成员都在这个学校const int ID;public:Student(int id): ID(id) // 所有类对象都会有不同的 ID{}// 打印学生信息void showInfo() const // 提高程序运行效率,只要确保该函数不会修改数据,就可以使用 const{cout << "我的学校" << schoolName << endl;cout << "我的ID" << ID << endl;}
};int main()
{Student *luo = new Student(1);luo->showInfo(); // 重要认知:类成员方法是可以直接使用类成员属性的,并不需要传参。return 0;
}注意:1、类方法的 const 关键字,要放在函数参数列表之后2、在类方法 声明 和 定义 中都需要加上 const ,因为这是重载的依据之一3、被 const 修饰的类方法不能访问类的非 const 方法,也不能对该类的其他数据成员赋值
类静态成员的设计和语法
当类成员被static
修饰时,被称为静态成员。(本质:限制其作用域范围)
类成员数据:表达类的属性,而不是具体对象的属性
类成员方法:表达类的行为,而不是具体对象的行为
#include <iostream>
using namespace std;class Student
{private:// 个人信息(单个对象属性)unsigned int ID;string name;// 群体信息(类类型本身的属性)static int total; // 总人数public:Student() { total++; }~Student() { total--; }// 获取学生总人数static void totalStudentNumber(); // 不依赖于类而存在{cout << "总人数:" << total << endl;}
};// 静态数据成员,必须在类外定义
int Student::total; // 本质:将全局的作用数据修改其作用域void totalStudentNumber();
{cout << "总人数:" << total << endl;
}int main()
{Student jack;Student::totalStudentNumber(); // 可以不依赖对象调用jack.totalStudentNumber(); // 也可以用类对象调用return 0;
}注意:1、静态类成员( 静态数据区 )与普通成员存储空间不一样2、静态类成员不属于任何一个对象,是共有的,只有一份3、静态类方法只能调用其他静态类方法或引用类静态数据,不能调用普通方法和数据(因为后者有归属者,说不定不存在)小补充:当构造函数被private修饰-------设计模式:单例模式
class Node
{public:static Node* getInstance();private:Node();private:static Node* m_ptr;
};Node* Node::m_ptr = NULL;Node::Node()
{cout << "123" << endl;
}Node* Node::getIstance()
{if(m_ptr == NULL){m_ptr = new Node; // 这样设定就是为了只被构造一次}return m_ptr;
}int main()
{Node* n = Node::getInstance();cout << n << endl;return 0;
}
初始化列表
#include <iostream>
using namespace std;class Score
{private:float math; // 数学成绩float history; // 历史成绩public:Score(float m=0, float h=0): math(m), history(h){}
};class Student
{private:int age; // 普通类成员const string name; // const型类成员Score score; // 类对象成员public:Student(int a, string n, Score &s) // & 这里采用引用就只构造一次: age(a), name(n), score(s) // 初始化列表{}showInfo() { cout << "数学和历史:" << math << history << endl; }
};int main()
{Score score(98, 89);Student student(22, "罗宏斌", score);student.showInfo();return 0;
}const 型类成员和类对象成员必须在初始化列表中进行初始化,为免去记忆,全部在初始化列表中初始化类组合
#include <iostream>
using namespace std;class AAA
{public:AAA() { cout << "AAA" << endl; }~AAA() { cout << "~AAA" << endl; }private:int m_AAA;
};class A
{public:A() {cout << "A" << endl;}~A() {cout << "~A" << endl;}private:int m_A;
};class B
{public:B(int n) : m_B(n) {cout << "B" << endl;}~B() {cout << "~B" << endl;}private:int m_B;
};class C
{public:// 不仅要负责对本类中的基本类型成员数据赋初值,也要对对象成员初始化。C(int x, int y) : b(x), m_C(y) { cout << "C" << endl; }~C() {cout << "~C" << endl;}private:A a;B b;AAA* aaa;int m_C;
};int main(int argc, char *argv[])
{// 类对象产生时,其构造顺序:// 1、先构造成员变量// 2、再调用构造函数C c(11, 12);return 0;
}