赞
踩
封装的意义
struct和class的区别
- #include<iostream>
- #include<string>
- using namespace std;
-
- class Student
- {
- private:
- string mName;
- int mAge;
- public:
- // 无参构造函数
- Student() {
- cout << "调用无参构造函数" << endl;
- };
- };
有参构造函数
- #include<iostream>
- #include<string>
- using namespace std;
-
- class Student
- {
- private:
- string mName;
- int mAge;
- public:
- // 有参构造函数
- Student(string name, int age) {
- cout << "调用有参构造函数" << endl;
- mName = name;
- mAge = age;
- }
- string getName()
- {
- return mName;
- }
-
- int getAge()
- {
- return mAge;
- }
- // 析构函数
- ~Student() {};
- };
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Student
- {
- private:
- string mName;
- int mAge;
- public:
- // 拷贝构造函数
- Student(const Student &s) {
- mName = s.mName;
- mAge = s.mAge;
- };
-
- string getName()
- {
- return mName;
- }
-
- int getAge()
- {
- return mAge;
- }
- // 析构函数
- ~Student() {};
- };
-
- int main()
- {
- Student s1 = Student("lisi", 23);
- // 调用拷贝构造函数
- Student s2 = s1;
- // 不能使用匿名对象构造拷贝构造方法,编译器会把代码转换为Student s2; 认为重复定义了一个s2的变量。
- //Student(s2); // 错误的代码
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
- class CDate
- {
- public:
- CDate(int year, int month, int day)
- {
- _year = year;
- _month = month;
- _day = day;
- }
- private:
- int _year;
- int _month;
- int _day;
- };
-
- class CGoods
- {
- public:
- CGoods(const char* n, int a, double p, int year, int month, int day):
- _date(year, month, day), // 这行代码相当于 CDate _date(year, month, day),即指定有参构造函数构造CDate对象,
- _amount(a),
- _price(p)
- {
- strcpy(_name, n); // _name成员变量必须要放到构造函数体中初始化
- // _date = CDate(year, month, day) // 如果写在构造函数体中,相当于 CDate _date; _date = CDate(year, month, day),
- // 即先构造一个CDate对象(默认使用无参构造函数),然后再给这个对象赋值。
- // 但是CDate并没有默认构造函数,因此在编译的时候就会报错。
- }
- private:
- char _name[20];
- int _amount;
- double _price;
- CDate _date; // 成员对象
- };
-
- #if 0
- int main()
- {
- CGoods good("沙发", 1, 12000, 2022, 1, 1);
- }
- #endif // 0
成员变量的初始化顺序只与其在类中被声明的顺序有关,而与在初始化列表的顺序无关。
- //
- // main.cpp
- // 数据成员的初始化顺序问题
- //
- // Created by apple on 2023/10/7.
- //
-
- #include <iostream>
- #include <string>
-
- using namespace std;
-
- class Point
- {
- public:
- Point(int ix):_iy(ix),_ix(_iy)// 会先初始化_ix,再初始化_iy
- {
- cout << "Point(int,int)" << endl;
- }
- void print()
- {
- cout << _ix << "\t" << _iy << endl;
- }
-
- private:
- int _ix;
- int _iy;
- };
-
- int main(int argc, const char * argv[]) {
-
- Point p(3);
- p.print();
- return 0;
- }
打印结果如下:
Point(int,int)
1 3
使用一个已经创建完成的对象来初始化一个新对象
- Student s1 = Student("lisi", 23);
- // 调用拷贝构造函数
- Student s2 = s1;
值传递的方式给函数参数传值
- #include <iostream>
- #include <string>
- using namespace std;
-
- class Student
- {
- public:
- int age;
- int* p_height;
- // 构造函数
- // 无参构造函数
- Student() {
- cout << "调用无参构造函数" << endl;
- };
- // 有参构造函数
- Student(int a, int height) {
- cout << "调用有参构造函数" << endl;
- age = a;
- p_height = new int(height);
- }
- // 自定义拷贝构造函数
- Student(const Student &s) {
- cout << "调用自定义的拷贝构造函数" << endl;
- age = s.age;
- p_height = new int(*s.p_height);
- };
- //析构函数
- ~Student() {
- // 在析构函数中可以做一些资源释放的工作
- if (p_height != nullptr) {
- delete p_height;
- p_height = nullptr;
- }
- cout << "调用析构函数" << endl;
- };
- };
-
- void func(Student s) // 值传递
- {
- cout << s.age <<"\t"<< *s.p_height << endl;
- }
-
- int main()
- {
- // 显式构造对象
- Student s = Student(23, 170);
- // 括号法构造对象
- Student s1 (23, 170);
- // 以值的方式将对象传递给函数参数,会调用拷贝构造函数
- func(s);
- }
输出结果如下:
- 调用有参构造函数
-
- 调用有参构造函数
-
- 调用自定义的拷贝构造函数
-
- 23 170
-
- 调用析构函数
-
- 调用析构函数
-
- 调用析构函数
以值方式返回局部对象
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Student
- {
- public:
- int age;
- int* p_height;
- // 构造函数
- // 无参构造函数
- Student() {
- cout << "调用无参构造函数" << endl;
- };
- // 有参构造函数
- Student(int a, int height) {
- cout << "调用有参构造函数" << endl;
- age = a;
- p_height = new int(height);
- }
- // 自定义拷贝构造函数
- Student(const Student &s) {
- cout << "调用自定义的拷贝构造函数" << endl;
- age = s.age;
- p_height = new int(*s.p_height);
- };
- //析构函数
- ~Student() {
- // 在析构函数中可以做一些资源释放的工作
- if (p_height != nullptr) {
- delete p_height;
- p_height = nullptr;
- }
- cout << "调用析构函数" << endl;
- };
- };
-
- Student func1()
- {
- Student s = Student(21, 180);
- // 以值的方式返回局部对象,会调用拷贝函数
- return s;
- }
-
- int main()
- {
- Student s = func1();
- }
拷贝构造函数形式:类名(const 类名 & rhs)
是否可以去掉引用符号?即将其改为 类名(const 类名 rhs)
,答案是不可以,因为会产生构造函数无穷递归调用的情况。
当执行 Point pt2 = pt 时,会调用拷贝构造函数,然后拷贝构造函数的形参会初始化,初始化形参又会调用拷贝构造函数,这样无穷递归下去,直到栈溢出。
是否可以去掉const关键字?即将其改为 类名( 类名 & rhs)
,答案是不可以,因为非const引用不能绑定右值。
假设 func()函数的返回值是一个 Point 对象,当执行 Point pt2 = func() 时,会调用拷贝构造函数,然而当给拷贝构造函数传递参数时, 如果没有 const,Point & rp = func() 是不正确的,因为 func()是一个临时对象,是右值,非const引用不能绑定右值。
总结:
Student("zhaoliu", 21); // 匿名对象
Student s1("zhangsan", 20); // 括号法
Student s2 = Student("lisi", 18); // 显式构造
Student s3 = {"wangwu", 19}; // 隐式转换
括号法
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Student
- {
- public:
- // 无参构造函数
- Student() {
- cout << "调用无参构造函数" << endl;
- };
- // 有参构造函数
- Student(string n, int a) {
- cout << "调用有参构造函数" << endl;
- name = n;
- age = a;
- }
- // 拷贝构造函数
- Student(const Student &s) {
- name=s.name;
- age = s.age;
- };
-
- string getName()
- {
- return name;
- }
-
- int getAge()
- {
- return age;
- }
- // 析构函数
- ~Student() {};
-
- private:
- string name;
- int age;
- };
- int main()
- {
- // 实例化对象:括号法
- // 调用无参构造方法时,不能加"()",因为编译器认为这是一个函数的声明
- Student s1;
- // 调用有参构造方法
- Student s2("zhansan", 22);
- cout << s2.getName() << "\t"<<s2.getAge() << endl;
-
- // 匿名对象,当前行执行结束之后,系统会立即收掉匿名对象
- Student("wangwu", 23);
-
- }
显示法
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Student
- {
- public:
- // 无参构造函数
- Student() {
- cout << "调用无参构造函数" << endl;
- };
- // 有参构造函数
- Student(string n, int a) {
- cout << "调用有参构造函数" << endl;
- name = n;
- age = a;
- }
- // 拷贝构造函数
- Student(const Student &s) {
- name=s.name;
- age = s.age;
- };
-
- string getName()
- {
- return name;
- }
-
- int getAge()
- {
- return age;
- }
- // 析构函数
- ~Student() {};
-
- private:
- string name;
- int age;
- };
- int main()
- {
- // 实例化对象:显示法
- Student s3 = Student("lisi", 23);
- cout << s3.getName() << "\t" << s3.getAge() << endl;
- // 匿名对象,当前行执行结束之后,系统会立即收掉匿名对象
- Student("wangwu", 23);
- }
隐式转换法
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Student
- {
- public:
- // 无参构造函数
- Student() {
- cout << "调用无参构造函数" << endl;
- };
- // 有参构造函数
- Student(string n, int a) {
- cout << "调用有参构造函数" << endl;
- name = n;
- age = a;
- }
- // 拷贝构造函数
- Student(const Student &s) {
- name=s.name;
- age = s.age;
- };
-
- string getName()
- {
- return name;
- }
-
- int getAge()
- {
- return age;
- }
- // 析构函数
- ~Student() {};
-
- private:
- string name;
- int age;
- };
- int main()
- {
- // 实例化对象:隐式转换法,如果有参构造函数只有一个参数,就不需要加"{}",但是可能会导致问题,不推荐。
- Student s5 = { "zhaoliu", 26 };
- }
不带 explicit 关键字
- class MyClass {
- public:
- MyClass(int x) {
- value = x;
- }
- private:
- int value;
- };
-
- void func(MyClass obj) {
- // do something
- }
-
- int main() {
- MyClass a = 10; // 隐式调用MyClass(int)构造函数
- func(10); // 隐式将int转换为MyClass对象
- return 0;
- }
带 explicit 关键字
- class MyClass {
- public:
- explicit MyClass(int x) {
- value = x;
- }
- private:
- int value;
- };
-
- void func(MyClass obj) {
- // do something
- }
-
- int main() {
- MyClass a = 10; // 错误:不能隐式转换int到MyClass
- MyClass b(10); // 正确:显式调用构造函数
- func(10); // 错误:不能隐式转换int到MyClass
- func(MyClass(10)); // 正确:显式创建MyClass对象
- return 0;
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Student
- {
- public:
- int age;
- int* p_height;
- // 无参构造函数
- Student() {
- cout << "调用无参构造函数" << endl;
- };
- // 有参构造函数
- Student(int a, int height) {
- cout << "调用有参构造函数" << endl;
- age = a;
- // 在堆内存中创建一块空间
- p_height = new int(height);
- }
- // 自定义拷贝构造函数
- Student(const Student &s) {
- cout << "调用自定义的拷贝构造函数" << endl;
- age = s.age;
- // p_height = s.p_height 编译器默认实现的就是这行代码
- p_height = new int(*s.p_height); // 自定义实现深拷贝
- };
-
- //析构函数
- ~Student() {
- // 在析构函数中可以做一些资源释放的工作
- if (p_height != nullptr) {
- // 释放内存
- delete p_height;
- p_height =nullptr;
- }
- cout << "调用析构函数" << endl;
- };
- };
-
- void test()
- {
- Student s1(18, 170);
- cout << "s1对象的年龄:" << s1.age << ",s1对象的身高:" << *s1.p_height << endl;
- // Student s2 = s1;
- Student s2(s1);
- // 如果没有自定义拷贝构造函数,默认是浅拷贝,当s2对象执行析构函数之后,p_height所指向的内存就被释放了
- // 然后,当s1对象执行析构函数时,再释放p_height的内存就是非法操作了,因此会报错,解决的办法就是深拷贝。
- // 通过自定义拷贝构造函数来实现深拷贝
- cout << "s2对象的年龄:" << s2.age << ",s2对象的身高:" << *s2.p_height << endl;
- }
- int main()
- {
- test();
- }
- class Computer
- {
- public:
- Computer(const char *brand, double price)
- : _brand(new char[strlen(brand) + 1]())
- , _price(price)
- {
- cout << "Computer(const char *, double)" << endl;
- }
- ~Computer()
- {
- cout << "~Computer()" << endl;
- delete [] _brand;
- _brand = nullptr;
- }
- Computer &Computer::operator=(const Computer &rhs)
- {
- if(this != &rhs) //1、自复制
- {
- delete [] _brand; //2、释放左操作数
- _brand = nullptr;
- _brand = new char[strlen(rhs._brand) + 1](); //3、深拷贝
- strcpy(_brand, rhs._brand);
- _price = rhs._price;
- }
- return *this; //4、返回*this
- }
- private:
- char *_brand;
- double _price;
- };
- class MyClass {
- public:
- MyClass operator=(const MyClass& other) {
- if (this != &other) {
- // 执行赋值操作
- }
- return *this; // 这里会涉及一次拷贝
- }
- };
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Student
- {
- public:
- string s_name;
- int s_age;
-
- // 初始化列表, s_name(name)相当于string s_name=name, s_age(a)相当于int s_age = a;
- Student(string name, int a) :s_name(name), s_age(a)
- {
- cout << "调用有参构造函数" << endl;
- }
- };
-
- int main()
- {
- // 括号法创建对象
- Student s1 ("zhangsan", 18);
- // 显示法创建对象
- Student s2 = Student("lisi", 19);
- cout << "s_name:" << s1.s_name << endl;
- cout << "s_age:" << s1.s_age << endl;
- }
const成员变量必须要放在初始化列表中进行初始化。
- class Book{
- public:
- Book( int s );
- private:
- int i;
- const int j;
- int &k;
-
- Book::Book( int s ): i(s), j(s), k(s){}
引用成员变量也必须要放在初始化列表中进行初始化。
结论:当其他类对象作为本类成员,构造函数先构造其他类对象,再构造本对象;析构的顺序与构造相反。
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Person
- {
- public:
- // 静态成员变量,类内声明
- static int pAge;
- };
- // 类外初始化,注意初始化的语法
- int Person::pAge = 18;
-
- void test()
- {
- // 静态成员变量可以直接通过类名访问
- cout << Person::pAge << endl;
- }
-
- int main()
- {
- test();
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Person
- {
- public:
- // 静态成员函数,类内声明
- static void func();
- };
- // 类外初始化,注意初始化的语法
- void Person::func() {
- cout << "Person类的静态成员方法" << endl;
- }
-
-
- int main()
- {
- Person::func();
- }
virtual
- #include <iostream>
- #include <typeinfo>
-
- using namespace std;
-
- class Base
- {
- public:
- Base(int data = 10):ma(data){} // 构造函数
- virtual void show() { cout << "Base::show()" << endl; }
- void show(int) { cout << "Base::show(int)" << endl; }
- private:
- int ma;
- };
- class Derive :public Base
- {
- public:
- Derive(int data=20):Base(data), mb(data){}
- void show() { cout << "Derive::show()" << endl; }
- private:
- int mb;
- };
-
- #if 1
- int main()
- {
- Derive d(50);
- Base *pb = &d;
- pb->show(); // 动态绑定 Derive::show()
- pb->show(10); // 静态绑定 Base::show(int)
-
- cout << sizeof(Base) << endl;
- cout << sizeof(Derive) << endl;
-
- cout << typeid(pb).name() << endl; // 指针的类型 class Base*
- // 如果Base中没有虚函数,*pb识别的就是编译时期的类型,即Base
- // 如果Base中有虚函数,*pb识别的就是运行时期的类型 即RTTI指针指向的类型
- cout << typeid(*pb).name() << endl; // 指针指向的类型 class Derive
- }
- #endif
类中的普通函数在编译时就确定了地址了,即为静态绑定。c++的重载函数就是静态绑定,因为在编译的时候就需要确定调用的是哪个函数。
类中的虚函数在编译时不能确定函数地址,即为动态绑定。另外,必须使用指针(引用)的方式调用虚函数才会产生动态绑定。
- Base &rb1 = b;
- rb1.show();
- Base &rb2 = d;
- rb2.show();
-
- Derive *pd1 = &d;
- pd1->show();
由对象直接调用虚函数不会产生动态绑定,因为可以确定是哪个对象调用的。
- Base b;
- b.show(); // 静态绑定
- Derive d;
- d.show(); // 静态绑定
首先说一下虚函数依赖
从虚函数依赖可知,以下函数不能成为虚函数
this指针本质上是一个指针常量,即指向的对象是不可以再更改的,但是指向的对象的值是可以修改的。
每一个成员函数都拥有一个隐含的this指针,这个this指针作为函数的第一个参数。
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- const int NAME_LEN = 20;
-
- class CGoods
- {
- public:
- // 编译的时候会在第一个参数的位置添加this指针,void show(CGood* this);
- void show()
- {
- cout << "show方法" << endl;
- }
- // 编译的时候会在第一个参数的位置添加this指针,void setPrice(CGood* this, double price)
- void setPrice(double price)
- {
- _price = price;
- }
- private:
- char _name[NAME_LEN];
- double _price;
- int _amount;
- };
-
-
- #if 1
- int main()
- {
- CGoods good;
- good.show();
- }
- #endif
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Person
- {
- public:
- int age;
- Person(int age)
- {
- this->age = age;
- }
-
- // 引用作为函数的返回值 Person &p = person
- Person& addAge(int age)
- {
- // this指针
- this->age += age;
- // this指针的解引用就是当前对象
- return *this;
- }
- };
-
- void test()
- {
- Person p(10);
- p.addAge(10).addAge(10).addAge(10);
- cout << p.age << endl;
- }
- int main()
- {
- test();
- }
- std::unique_ptr<PaddleClasModel> PaddleClasModel::Clone() const {
- std::unique_ptr<PaddleClasModel> clone_model =
- utils::make_unique<PaddleClasModel>(PaddleClasModel(*this));
- clone_model->SetRuntime(clone_model->CloneRuntime());
- return clone_model;
PaddleClasModel(*this)调用的是默认的拷贝构造函数
- class PaddleClasModel {
- public:
- PaddleClasModel(const PaddleClasModel& other);
- };
常函数
常对象
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Person
- {
- public:
- int age;
- // 成员属性加了mutable关键字,即使在常函数中也是可以更改name属性的值的
- mutable string name;
- Person(int age, string name)
- {
- this->age = age;
- this->name = name;
- }
-
- // 常函数,使用const修饰,本质上就是const Person * const p;因此既不可以更改对象,也不可以更改对象的属性值
- void func() const
- {
- cout << "调用常函数" << endl;
- //this->age = 20; 错误,常函数不可以修改成员属性
- // name属性是可以修改的
- this->name = "wangwu";
- }
-
- void func2()
- {
- cout << "调用普通函数" << endl;
- }
-
- };
-
- void test()
- {
- Person p(10, "lisi");
- p.func();
- cout << p.name << endl;
- // 常对象
- const Person p2(20, "zhaoliu");
- //p2.func2(); 那么
- p2.func();
-
- }
- int main()
- {
- test();
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
-
- class Person
- {
- public:
- int age;
- void showClass()
- {
- cout << "this is Person class" << endl;
- }
-
- void showPersonAge()
- {
- // age 默认指的是this->age既然this都是空了,那么访问this->age肯定就会报错了
- cout << "age = " << age << endl;
- }
- };
-
- void test()
- {
- Person *p = nullptr;
- //p->showClass(); // 正确
- //p->showPersonAge(); // 报错
- }
-
- int main()
- {
- test();
- }
- #define _CRT_SECURE_NO_WARNINGS
- #include <iostream>
-
-
- using namespace std;
-
- class String
- {
- public:
- String(const char *str=nullptr)
- {
- if (str != nullptr)
- {
- m_data = new char[strlen(str) + 1];
- strcpy(this->m_data, str);
- }
- else
- {
- m_data = new char[1];
- *m_data = '\0';
- }
- }
- // 拷贝构造函数
- String(const String& str)
- {
- m_data = new char[strlen(str.m_data) + 1];
- strcpy(m_data, str.m_data);
- }
- // 析构函数
- ~String()
- {
- delete[] m_data;
- m_data = nullptr;
- }
- // 赋值构造函数
- String& operator=(const String & other)
- {
- if (this == &other)
- {
- return *this;
- }
- delete[] m_data; // 先释放之前指向堆内存的空间
- m_data = new char[strlen(other.m_data) + 1]; // 再重新申请一块堆空间,c语言的字符串最后有一个'\0'字符,因此需要多一个字符的长度
- strcpy(this->m_data, other.m_data); // c语言的strcpy函数,再将other的m_data值拷贝到堆空间
- return *this;
- }
- char* m_data;
- };
-
- #if 1
- int main()
- {
- String s1; // 调用无参构造函数
- String s2("hello"); // 调用有参构造函数
- String s3(s2); // 调用拷贝构造函数
- s3 = s1; // 调用赋值构造函数
- }
- #endif
- #include <iostream>
-
- using namespace std;
-
- class Test
- {
- public:
- int mb;
- static int si;
- void func() { cout << "call Test::func" << endl; }
- static void static_func() { cout << "call Test::static_func" << endl; }
- };
-
- int Test::si = 20;
-
- int main()
- {
- Test t1;
- int Test::*p = &Test::mb; // 如果写成int *p = &Test::mb; 会报错,无法从 int Test::* 转换成 int *
- // 指针指向普通成员变量,脱离对象访问成员是没有意义的,因此,在访问p时必须加上对象,不能直接是*p=20
- t1.*p = 20;
- cout << t1.mb << endl;
-
- // 指针指向静态成员变量,这里就可以这样写了
- int *p1 = &Test::si;
- *p1 = 30;
- cout << Test::si << endl;
- // 指针指向普通成员方法
- void(Test::*func)() = &Test::func; // 如果写成void(*func)() = &Test::func; 会报错,无法从 void (__thiscall Test::*)(void)转换成void (__cdecl *)(void)
- (t1.*func)();
- // 指针指向静态成员方法
- void(*static_func)() = &Test::static_func;
- (*static_func)();
-
- }
友元提供了另一访问类的私有成员的方案
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Building
- {
- // 全局函数做友元,告诉编译器,全局函数goodGay是Building类的好朋友,可以访问类中的私有内容
- friend void goodGay(Building building);
- public:
- string m_SittingRoom;
- Building()
- {
- this->m_SittingRoom = "客厅";
- this->m_BedRom = "卧室";
-
- }
- private:
- string m_BedRom;
- };
-
- void goodGay(Building building)
- {
- cout << "好基友正在访问:" << building.m_SittingRoom << endl;
- cout << "好基友正在访问:" << building.m_BedRom << endl;
- }
-
- int main()
- {
- Building building;
- goodGay(building);
- }
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Building;
-
- class GoodGay
- {
- public:
- // 这里只是声明了构造方法
- GoodGay();
- // 这里只是声明了成员函数
- void visit();
- private:
- // 成员变量
- Building *building;
- };
- class Building
- {
- // 友元类,告诉编译器GoodGay类可以访问Building类中的私有内容
- friend class GoodGay;
- public:
- // 声明构造方法
- Building();
-
- public:
- // 公共的成员变量
- string m_sittingRoom;
- private:
- // 私有的成员变量
- string m_bedroom;
- };
- // 在类的外部定义Building构造函数
- Building::Building()
- {
- this->m_bedroom = "卧室";
- this->m_sittingRoom = "客厅";
- }
- // 在类的外部定义GoodGay构造函数
- GoodGay::GoodGay() {
- building = new Building();
- }
- // 在类的外部定义visit方法
- void GoodGay::visit()
- {
- cout << "好基友正在访问" << building->m_bedroom << endl;
- cout << "好基友正在访问" << building->m_sittingRoom << endl;
- }
-
- int main()
- {
- GoodGay goodGay = GoodGay();
- goodGay.visit();
- }
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Building;
-
- class GoodGay
- {
- public:
- // 这里只是声明了构造方法
- GoodGay();
- // 这里只是声明了成员函数
- void visit();
- private:
- // 成员变量
- Building *building;
- };
- class Building
- {
- // 告诉编译器GoodGay类中的成员函数visit可以访问Building类中的私有内容
- friend void GoodGay::visit();
- public:
- // 声明构造方法
- Building();
-
- public:
- // 公共的成员变量
- string m_sittingRoom;
- private:
- // 私有的成员变量
- string m_bedroom;
- };
- // 在类的外部定义Building构造函数
- Building::Building()
- {
- this->m_bedroom = "卧室";
- this->m_sittingRoom = "客厅";
- }
- // 在类的外部定义GoodGay构造函数
- GoodGay::GoodGay() {
- // 无参构造函数"()"可以省略
- //building = new Building;
- building = new Building();
-
- }
- // 在类的外部定义visit方法
- void GoodGay::visit()
- {
- cout << "好基友正在访问" << building->m_bedroom << endl;
- cout << "好基友正在访问" << building->m_sittingRoom << endl;
- }
-
- int main()
- {
- GoodGay goodGay = GoodGay();
- goodGay.visit();
- }
注意事项:
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Person
- {
- public:
- int m_a;
- int m_b;
- // 使用成员函数重载+运算符
- //Person operator+(Person &p)
- //{
- // Person temp;
- // temp.m_a = m_a + p.m_a;
- // temp.m_b = m_b + p.m_b;
- // return temp;
- //}
- };
-
- // 使用全局函数重载+运算符
- 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;
- }
-
- // 运算符重载的函数重载
- Person operator+(Person &p1, int num)
- {
- Person temp;
- temp.m_a = p1.m_a + num;
- temp.m_b = p1.m_b + num;
- return temp;
- }
- int main()
- {
- // 使用括号法调用无参构造函数不能加括号,因此是Person p1,而不是Person p1()
- Person p1;
- p1.m_a = 10;
- p1.m_b = 10;
- Person p2;
- p2.m_a = 10;
- p2.m_b = 10;
-
- Person p3 = p1 + p2;
- cout << "p3.m_a = " << p3.m_a << "\t" << "p3.m_b = " << p3.m_b << endl;
-
- Person p4 = p1 + 100;
- cout << "p4.m_a = " << p4.m_a << "\t" << "p4.m_b = " << p4.m_b << endl;
- }
作用:
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Person
- {
- // 全局函数做友元,可以访问类中的私有属性
- friend ostream & operator<<(ostream &out, Person &p);
- private:
- int m_a;
- int m_b;
- public:
- void setA(int a)
- {
- m_a = a;
- }
-
- void setB(int b)
- {
- m_b = b;
- }
- };
-
- // 使用全局函数重载+运算符
- ostream & operator<<(ostream &out, Person &p)
- {
- out << "p.m_a = " << p.m_a << ", p.m_b = " << p.m_b;
- return out;
- }
-
- int main()
- {
- // 使用括号法调用无参构造函数不能加括号,因此是Person p1,而不是Person p1()
- Person p1;
- p1.setA(10);
- p1.setB(10);
- cout << p1 << endl;
- }
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class MyInteger
- {
- // 全局函数做友元,可以访问类中的私有属性
- friend ostream & operator<<(ostream &out, MyInteger myint);
- private:
- int m_a;
- public:
- MyInteger()
- {
- m_a = 1;
- }
- // 重载前置++运算符,这里必须返回引用,即同一个对象
- MyInteger& operator++()
- {
- ++m_a;
- return *this;
- }
- // 重载后置++运算符
- MyInteger operator++(int) //int代表占位参数
- {
- MyInteger temp = *this; // 先保存当前对象 *this 就表示当前对象
- m_a++; // 然后再让对象中的m_a的值自增
- return temp;
- }
- };
-
- // 使用全局函数重载<<运算符
- ostream & operator<<(ostream &out, MyInteger myint)
- {
- out << "myint.m_a = " << myint.m_a;
- return out;
- }
-
- int main()
- {
- MyInteger myint;
- cout << ++myint << endl;
- cout << myint << endl;
-
- cout << myint++ << endl;
- cout << myint << endl;
- }
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class Person
- {
- public:
- int *m_age;
- public:
- Person(int age)
- {
- // new int 返回的是int类型的指针
- m_age = new int(age);
- }
-
- ~Person()
- {
- if (m_age != nullptr)
- {
- delete m_age;
- m_age = nullptr;
- }
- }
-
- // 重载赋值运算符
- Person& operator=(Person &p)
- {
- if (m_age != nullptr) {
- delete m_age; // 释放m_age的内存
- m_age = new int(*p.m_age); // 重新拷贝一份,放在堆内存,在拷贝之前需要将this指针指向的对象的m_age属性的空间给释放,防止野指针
- return *this; // 为了能够链式调用,需要返回对象的引用
- }
- }
-
- };
-
- int main()
- {
- Person p1(10);
- Person p2(20);
- Person p3(30);
-
- // 默认是浅拷贝,在析构函数中清空内存就会存在问题,因此需要手动改为深拷贝
- p3 = p2 = p1;
-
- cout << "p1的年龄为:" << *p1.m_age << endl;
- cout << "p2的年龄为:" << *p2.m_age << endl;
- cout << "p3的年龄为:" << *p3.m_age << endl;
-
- }
void* operator new(size_t size)
void operator delete(void* ptr)
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- void* operator new(size_t size) // 参数必须是size_t(unsigned long long),返回值必须是void*。是静态成员函数
- {
- cout << "调用了全局重载的new:" << size << "字节。\n";
- // 申请内存
- void* ptr = malloc(size);
- cout << "申请到的内存的地址是:" << ptr << endl;
- return ptr;
- }
-
- void operator delete(void* ptr) // 参数必须是void *,返回值必须是void。是静态成员函数
- {
- cout << "调用了全局重载的delete。\n";
- // 判断是否是空指针
- if (ptr == 0) return; // 对空指针delete是安全的。
- free(ptr); // 释放内存。
- }
-
- class CGirl // 超女类CGirl。
- {
- public:
- int m_bh; // 编号。
- int m_xw; // 胸围。
-
- CGirl(int bh, int xw) { m_bh = bh, m_xw = xw; cout << "调用了构造函数CGirl()\n"; }
- ~CGirl() { cout << "调用了析构函数~CGirl()\n"; }
- void* operator new(size_t size)
- {
- cout << "调用了类的重载的new:" << size << "字节。\n";
- // 申请内存
- void* ptr = malloc(size);
- cout << "申请到的内存的地址是:" << ptr << endl;
- return ptr;
- }
-
- void operator delete(void* ptr) // 参数必须是void *,返回值必须是void。
- {
- cout << "调用了类的重载的delete。\n";
- // 判断是否是空指针
- if (ptr == 0) return; // 对空指针delete是安全的。
- free(ptr); // 释放内存。
- }
- };
-
-
- int main()
- {
- // 会调用全局重载函数new
- int* p1 = new int(3);
- // 会调用全局重载函数delete
- delete p1;
-
- CGirl* p2 = new CGirl(3, 8);
- cout << "p2的地址是:" << p2 << "编号:" << p2->m_bh << ",胸围:" << p2->m_xw << endl;
- delete p2;
- }
- #include<iostream>
- #include<string>
-
- using namespace std;
-
- class CGirl // 超女类CGirl。
- {
- public:
- int m_bh; // 编号。
- string m_name; // 姓名。
- double m_weight; // 体重,单位:kg。
-
- // 默认构造函数。
- CGirl() { m_bh = 0; m_name.clear(); m_weight = 0; cout << "调用了CGirl()\n"; }
- // 自我介绍的方法。
- void show() { cout << "bh=" << m_bh << ",name=" << m_name << ",weight=" << m_weight << endl; }
- // 关闭自动类型转换,但是可以显式转换
- explicit CGirl(int bh) { m_bh = bh; m_name.clear(); m_weight = 0; cout << "调用了CGirl(int bh)\n"; }
- //CGirl(double weight) { m_bh = 0; m_name.clear(); m_weight = weight; cout << "调用了CGirl(double weight)\n"; }
- };
- int main()
- {
- //CGirl g1(8); // 常规的写法。
- //CGirl g1 = CGirl(8); // 显式转换。
- //CGirl g1 = 8; // 隐式转换。
- CGirl g1; // 创建对象。
- g1 = (CGirl)8; // 隐式转换,用CGirl(8)创建临时对象,再赋值给g。
- //CGirl g1 = 8.7; // 隐式转换。
- //g1.show();
- }
class 派生类名:[继承方式]基类名
默认是private。不管继承方式如何,基类中的private成员在派生类中始终不能使用。
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- class A { // 基类
- public:
- int m_a;
- private:
- int m_b;
- public:
- A() : m_a(0), m_b(0) // 基类的默认构造函数。
- {
- cout << "调用了基类的默认构造函数A()。\n";
- }
- A(int a, int b) : m_a(a), m_b(b) // 基类有两个参数的构造函数。
- {
- cout << "调用了基类的构造函数A(int a,int b)。\n";
- }
- A(const A &a) : m_a(a.m_a + 1), m_b(a.m_b + 1) // 基类的拷贝构造函数。
- {
- cout << "调用了基类的拷贝构造函数A(const A &a)。\n";
- }
-
- // 显示基类A全部的成员。
- void showA() { cout << "m_a=" << m_a << ",m_b=" << m_b << endl; }
- };
-
- class B :public A // 派生类
- {
- public:
- int m_c;
- B() : m_c(0), A() // 派生类的默认构造函数,指明用基类的默认构造函数(不指明也无所谓)。顺序也无所谓
- {
- cout << "调用了派生类的默认构造函数B()。\n";
- }
- B(int a, int b, int c) : A(a, b), m_c(c) // 指明用基类的有两个参数的构造函数。
- {
- cout << "调用了派生类的构造函数B(int a,int b,int c)。\n";
- }
- B(const A& a, int c) :A(a), m_c(c) // 指明用基类的拷贝构造函数。
- {
- cout << "调用了派生类的构造函数B(const A &a,int c) 。\n";
- }
-
- // 显示派生类B全部的成员。
- void showB() { cout << "m_c=" << m_c << endl << endl; }
- };
-
- int main()
- {
- B b1; // 将调用基类默认的构造函数。
- b1.showA(); b1.showB();
-
- B b2(1, 2, 3); // 将调用基类有两个参数的构造函数。
- b2.showA(); b2.showB();
-
- A a(10, 20); // 创建基类对象。
- B b3(a, 30); // 将调用基类的拷贝造函数。
- b3.showA(); b3.showB();
- }
注意事项:
class 子类: 继承方式 父类1, 继承方式 父类2 ...
c++实际开发中不建议用多继承
通过基类只能访问派生类的成员变量,但是不能访问派生类的成员函数。
为了消除这种尴尬,让基类指针能够访问派生类的成员函数,C++ 增加了虚函数(Virtual Function)。使用虚函数非常简单,只需要在函数声明前面增加 virtual 关键字。
注意事项:
- #include <iostream>
- using namespace std;
-
- //军队
- class Troops {
- public:
- // 基类设置为虚函数
- virtual void fight() { cout << "Strike back!" << endl; }
- };
-
- //陆军
- class Army : public Troops {
- public:
- void fight() { cout << "--Army is fighting!" << endl; }
- };
- //99A主战坦克
- class _99A : public Army {
- public:
- void fight() { cout << "----99A(Tank) is fighting!" << endl; }
- };
- //武直10武装直升机
- class WZ_10 : public Army {
- public:
- void fight() { cout << "----WZ-10(Helicopter) is fighting!" << endl; }
- };
- //长剑10巡航导弹
- class CJ_10 : public Army {
- public:
- void fight() { cout << "----CJ-10(Missile) is fighting!" << endl; }
- };
-
- //空军
- class AirForce : public Troops {
- public:
- void fight() { cout << "--AirForce is fighting!" << endl; }
- };
- //J-20隐形歼击机
- class J_20 : public AirForce {
- public:
- void fight() { cout << "----J-20(Fighter Plane) is fighting!" << endl; }
- };
- //CH5无人机
- class CH_5 : public AirForce {
- public:
- void fight() { cout << "----CH-5(UAV) is fighting!" << endl; }
- };
- //轰6K轰炸机
- class H_6K : public AirForce {
- public:
- void fight() { cout << "----H-6K(Bomber) is fighting!" << endl; }
- };
-
- int main() {
- // 基类指针
- Troops* p = new Troops;
- p->fight();
- //陆军
- p = new Army;
- p->fight();
- p = new _99A;
- p->fight();
- p = new WZ_10;
- p->fight();
- p = new CJ_10;
- p->fight();
- //空军
- p = new AirForce;
- p->fight();
- p = new J_20;
- p->fight();
- p = new CH_5;
- p->fight();
- p = new H_6K;
- p->fight();
-
- return 0;
- }
语法格式:virtual 返回值类型 函数名 (函数参数) = 0;
纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上=0,表明此函数为纯虚函数。
包含纯虚函数的类称为抽象类(Abstract Class)。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。
抽象类通常是作为基类,让派生类去实现纯虚函数。派生类必须实现纯虚函数才能被实例化。
- #include <iostream>
- using namespace std;
-
- //线
- class Line {
- public:
- Line(float len);
- virtual float area() = 0;
- virtual float volume() = 0;
- protected:
- float m_len;
- };
- //类外定义构造函数
- Line::Line(float len) : m_len(len) { }
-
- //矩形,继承线类,也是一个抽象类,不能实例化对象
- class Rec : public Line {
- public:
- Rec(float len, float width);
- float area();
- protected:
- float m_width;
- };
- Rec::Rec(float len, float width) : Line(len), m_width(width) { }
- float Rec::area() { return m_len * m_width; }
-
- //长方体
- class Cuboid : public Rec {
- public:
- Cuboid(float len, float width, float height);
- float area();
- float volume();
- protected:
- float m_height;
- };
- Cuboid::Cuboid(float len, float width, float height) : Rec(len, width), m_height(height) { }
- float Cuboid::area() { return 2 * (m_len * m_width + m_len * m_height + m_width * m_height); }
- float Cuboid::volume() { return m_len * m_width * m_height; }
-
- //正方体
- class Cube : public Cuboid {
- public:
- Cube(float len);
- float area();
- float volume();
- };
- Cube::Cube(float len) : Cuboid(len, len, len) { }
- float Cube::area() { return 6 * m_len * m_len; }
- float Cube::volume() { return m_len * m_len * m_len; }
-
- int main() {
- //Line* p0 = new Rec(10, 20); // error
- Line* p = new Cuboid(10, 20, 30);
- cout << "The area of Cuboid is " << p->area() << endl;
- cout << "The volume of Cuboid is " << p->volume() << endl;
-
- p = new Cube(15);
- cout << "The area of Cube is " << p->area() << endl;
- cout << "The volume of Cube is " << p->volume() << endl;
-
- return 0;
- }
运行结果:
The area of Cuboid is 2200
The volume of Cuboid is 6000
The area of Cube is 1350
The volume of Cube is 3375
本例中定义了四个类,它们的继承关系为:Line --> Rec --> Cuboid --> Cube。
Line 是一个抽象类,也是最顶层的基类,在 Line 类中定义了两个纯虚函数 area() 和 volume()。
在 Rec 类中,实现了 area() 函数;所谓实现,就是定义了纯虚函数的函数体。但这时 Rec 仍不能被实例化,因为它没有实现继承来的 volume() 函数,volume() 仍然是纯虚函数,所以 Rec 也仍然是抽象类。
直到 Cuboid 类,才实现了 volume() 函数,才是一个完整的类,才可以被实例化。
可以发现,Line 类表示“线”,没有面积和体积,但它仍然定义了 area() 和 volume() 两个纯虚函数。这样的用意很明显:Line 类不需要被实例化,但是它为派生类提供了“约束条件”,派生类必须要实现这两个函数,完成计算面积和体积的功能,否则就不能实例化。
在实际开发中,你可以定义一个抽象基类,只完成部分功能,未完成的功能交给派生类去实现(谁派生谁实现)。这部分未完成的功能,往往是基类不需要的,或者在基类中无法实现的。虽然抽象基类没有完成,但是却强制要求派生类完成,这就是抽象基类的“霸王条款”。
抽象基类除了约束派生类的功能,还可以实现多态。请注意第 51 行代码,指针 p 的类型是 Line,但是它却可以访问派生类中的 area() 和 volume() 函数,正是由于在 Line 类中将这两个函数定义为纯虚函数;如果不这样做,51 行后面的代码都是错误的。我想,这或许才是C++提供纯虚函数的主要目的。
关于纯虚函数的几点说明
1) 一个纯虚函数就可以使类成为抽象基类,但是抽象基类中除了包含纯虚函数外,还可以包含其它的成员函数(虚函数或普通函数)和成员变量。
2) 只有类中的虚函数才能被声明为纯虚函数,普通成员函数和顶层函数均不能声明为纯虚函数。如下例所示:
- //顶层函数不能被声明为纯虚函数
- void fun() = 0; //compile error
- class base{
- public :
- //普通成员函数不能被声明为纯虚函数
- void display() = 0; //compile error
- };
- #include<iostream>
- using namespace std;
-
- class ClxBase
- {
- public:
- ClxBase() {};
- virtual ~ClxBase() { cout << "delete ClxBase" << endl; };
- virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
- };
-
- class ClxDerived : public ClxBase
- {
- public:
- ClxDerived() {};
- ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
- void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
- };
-
- int main(int argc, char const* argv[])
- {
- ClxBase* pTest = new ClxDerived;
- pTest->DoSomething();
- delete pTest;
- return 0;
- }
打印结果如下:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
delete ClxBase
如果基类ClxBase的析构函数没有定义成虚函数,那么打印结果为:
Do something in class ClxDerived!
delete ClxBase
即不会调用派生类的析构函数,这样会造成数据泄露的问题。
虚析构函数的作用:
(1)如果父类的析构函数不加virtual关键字
当父类的析构函数不声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,只调用父类的析构函数,而不调用子类的析构函数。
(2)如果父类的析构函数加virtual关键字
当父类的析构函数声明成虚析构函数的时候,当子类继承父类,父类的指针指向子类时,delete掉父类的指针,先调用子类的析构函数,再调用父类的析构函数。
语法格式:派生类指针 = dynamic_cast<派生类类型 *>(基类指针);
如果转换成功,dynamic_cast返回对象的地址,如果失败,返回nullptr。
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- class Hero // 英雄基类
- {
- public:
- int viability; // 生存能力。
- int attack; // 攻击伤害。
- virtual void skill1() { cout << "英雄释放了一技能。\n"; }
- virtual void skill2() { cout << "英雄释放了二技能。\n"; }
- virtual void uskill() { cout << "英雄释放了大绝招。\n"; }
- };
-
- class XS :public Hero // 西施派生类
- {
- public:
- void skill1() { cout << "西施释放了一技能。\n"; }
- void skill2() { cout << "西施释放了二技能。\n"; }
- void uskill() { cout << "西施释放了大招。\n"; }
- void show() { cout << "我是天下第一美女。\n"; }
- };
-
- class HX :public Hero // 韩信派生类
- {
- public:
- void skill1() { cout << "韩信释放了一技能。\n"; }
- void skill2() { cout << "韩信释放了二技能。\n"; }
- void uskill() { cout << "韩信释放了大招。\n"; }
- };
-
- class LB :public Hero // 李白派生类
- {
- public:
- void skill1() { cout << "李白释放了一技能。\n"; }
- void skill2() { cout << "李白释放了二技能。\n"; }
- void uskill() { cout << "李白释放了大招。\n"; }
- };
-
- int main()
- {
- // 根据用户选择的英雄,施展一技能、二技能和大招。
- int id = 0; // 英雄的id。
- cout << "请输入英雄(1-西施;2-韩信;3-李白。):";
- cin >> id;
- // 创建基类指针,让它指向派生类对象,用基类指针调用派生类的成员函数。
- Hero* ptr = nullptr;
- if (id == 1) { // 1-西施
- ptr = new XS;
- }
- else if (id == 2) { // 2-韩信
- ptr = new HX;
- }
- else if (id == 3) { // 3-李白
- ptr = new LB;
- }
-
- if (ptr != nullptr) {
- ptr->skill1();
- ptr->skill2();
- ptr->uskill();
-
- // 如果基类指针指向的对象是西施,那么就调用西施的show()函数。
- //if (id == 1) {
- // XS* pxs = (XS *)ptr; // C风格强制转换的方法,程序员必须保证目标类型正确。
- // pxs->show();
- //}
- XS* xsptr = dynamic_cast<XS*>(ptr); // 把基类指针转换为派生类。
- if (xsptr != nullptr) xsptr->show(); // 如果转换成功,调用派生类西施的非虚函数。
-
- delete ptr;
- }
-
- // 以下代码演示把基类引用转换为派生类引用时发生异常的情况。
- /*HX hx;
- Hero& rh = hx;
- try{
- XS & rxs= dynamic_cast<XS &>(rh);
- }
- catch (bad_cast) {
- cout << "出现了bad_cast异常。\n";
- }*/
- }
注意:
1)dynamic_cast只适用于包含虚函数的类。
2)dynamic_cast可以将派生类指针转换为基类指针,这种画蛇添足的做法没有意义。
3)dynamic_cast可以用于引用,但是,没有与空指针对应的引用值,如果转换请求不正确,会出现bad_cast异常。
- template <typename T>
- void Swap(T &a, T &b)
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
- class CGirl
- {
- public:
- template<typename T>
- CGirl(T a)
- {
- cout << "a= " << a << endl;
- }
-
- template<typename T>
- void show()
- {
- cout << "show方法" << endl;
- }
-
- // 错误的
- //template<typename T>
- //virtual void show()
- //{
- // cout << "show方法" << endl;
- //}
-
- //template<typename T>
- //~CGirl()
- //{
-
- //}
- };
-
- int main()
- {
- int a = 10;
- CGirl g = CGirl(a);
- g.show<int>();
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
- template <typename T>
- void Swap(T &a, T &b) // 传引用
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
-
-
- int main()
- {
- // 错误的,传引用,必须是数据类型一致的,不能进行隐式转换
- //int a = 10;
- //char b = 30;
- //Swap(a, b);
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
- template <typename T>
- void Swap(T a, T b) // 传值
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
-
-
- int main()
- {
- // 正确的
- int a = 10;
- char b = 30;
- Swap<int>(a, b); // 可以发生隐式转换
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
- // 函数模板中没有用到模板参数
- template <typename T>
- void Swap()
- {
- cout << "调用了Swap函数" << endl;
- }
-
-
- int main()
- {
- // 错误的
- //Swap();
- // 正确的,显式的指定。
- Swap<int>();
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
- template <typename T>
- T Add(T a, T b)
- {
- return a + b;
- }
-
- class CGirl
- {
- };
- int main()
- {
- //错误的,CGirl对象没有+运算
- //CGirl g1;
- //CGirl g2;
- //Add(g1 + g2);
- }
- #include<iostream>
- #include<string>
- using namespace std;
-
- template <typename T>
- void Swap(T a, T b)
- {
- T tmp = a;
- a = b;
- b = tmp;
- }
-
-
- int main()
- {
- // 正确的
- int a = 10;
- char b = 30;
- Swap<int>(a, b); // 显式指定了int数据类型,可以发生从char到int的数据类型转换
- }
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- class CGirl // 超女类。
- {
- public:
- int m_bh; // 编号。
- string m_name; // 姓名。
- int m_rank; // 排名。
- };
-
- template <typename T>
- void Swap(T& a, T& b); // 交换两个变量的值函数模板。
-
- template<>
- void Swap<CGirl>(CGirl& g1, CGirl& g2); // 交换两个超女对象的排名。这个函数是函数模板的具体化函数
- // template<>
- // void Swap(CGirl& g1, CGirl& g2); // 交换两个超女对象的排名。
-
- int main()
- {
- int a = 10, b = 20;
- Swap(a, b); // 使用了函数模板。
- cout << "a=" << a << ",b=" << b << endl;
-
- CGirl g1, g2;
- g1.m_rank = 1; g2.m_rank = 2;
- Swap(g1, g2); // 使用了超女类的具体化函数。
- cout << "g1.m_rank=" << g1.m_rank << ",g2.m_rank=" << g2.m_rank << endl;
- }
编译器使用各种函数的规则:
- #include <iostream> // 包含头文件。
- #include<string>
- using namespace std; // 指定缺省的命名空间。
-
- void Swap(int a, int b) // 普通函数。
- {
- cout << "使用了普通函数。\n";
- }
-
- template <typename T>
- void Swap(T a, T b) // 函数模板。
- {
- cout << "使用了函数模板。\n";
- }
-
- template <>
- void Swap(int a, int b) // 函数模板的具体化版本。
- {
- cout << "使用了具体化的函数模板。\n";
- }
-
- int main()
- {
- Swap(1,2); // 会调用普通函数
- Swap('c', 'd'); // 会调用函数模板,因为不用进行隐式转换
- Swap<>(1,2); // 用空模板,会强制调用函数模板的具体化版本
- }
记住下面两点就可以了:
语法:decltype(expression) var;
作用:用于分析表达式的数据类型
- #include <iostream> // 包含头文件。
- #include<string>
- using namespace std; // 指定缺省的命名空间。
-
- template <typename T>
- auto Swap(T a, T b) // 函数模板。
- {
- decltype(a + b) temp = a + b;
- cout << "temp = " << temp << endl;
- return temp;
- }
-
-
- int main()
- {
- auto res = Swap('c', 'd');
- cout << res << endl;
- }
- #include <iostream> // 包含头文件。
- #include<string>
- using namespace std; // 指定缺省的命名空间。
-
- int func()
- {
- cout << "调用了func函数" << endl;
- return 3;
- }
-
-
- int main()
- {
- decltype(func()) f = func(); // 函数返回值的数据类型
- cout << f << endl;
-
- decltype(func) *f = func; // 函数类型
- f(); // 调用func函数
- }
- template <typename T>
- struct MakeUniqueResult {
- using scalar = std::unique_ptr<T>;
- };
-
- template <typename T, typename... Args>
- typename MakeUniqueResult<T>::scalar make_unique(Args &&... args) { // NOLINT
- return std::unique_ptr<T>(
- new T(std::forward<Args>(args)...)); // NOLINT(build/c++11)
- }
上面代码中,编译器无法自动区分 MakeUniqueResult<T>::scalar 是一个类型还是一个成员变量。为了明确告诉编译器 scalar 是一个类型,我们使用 typename 关键字。没有 typename,编译器会产生错误,因为它不能确定 scalar 的含义。
- template <class T>
- class 类模板名
- {
- 类的定义;
- };
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- template <class T1, class T2>
- class AA
- {
- public:
- T1 m_a; // 通用类型用于成员变量。
- T2 m_b; // 通用类型用于成员变量。
-
- AA() { } // 默认构造函数是空的。
- // 通用类型用于成员函数的参数。
- AA(T1 a, T2 b) :m_a(a), m_b(b) { }
- // 通用类型用于成员函数的返回值。
- T1 geta() // 获取成员m_a的值。
- {
- T1 a = 2; // 通用类型用于成员函数的代码中。
- return m_a + a;
- }
- T2 getb(); // 获取成员m_b的值。
- };
-
- // 模板类的成员函数可以在类外实现。
- template<class T1, class T2>
- T2 AA<T1, T2>::getb()
- {
- return m_b;
- }
- int main()
- {
- AA<int, string>* a; // 在创建对象的时候,必须指明具体的数据类型。AA a 是错误的。
- }
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
-
- template <class T1, class T2=string> // 指定通用数据类型的数据类型
- class AA
- {
- }
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- template <class T1, class T2>
- class AA
- {
- public:
- T1 m_a; // 通用类型用于成员变量。
- T2 m_b; // 通用类型用于成员变量。
-
- AA() { } // 默认构造函数是空的。
- // 通用类型用于成员函数的参数。
- AA(T1 a, T2 b) :m_a(a), m_b(b) { }
-
- T2 getb(); // 获取成员m_b的值。
- };
-
- // 模板类的成员函数可以在类外实现。
- template<class T1, class T2>
- T2 AA<T1, T2>::getb()
- {
- return m_b;
- }
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- template <class T1, class T2=string>
- class AA
- {
- public:
- T1 m_a; // 通用类型用于成员变量。
- T2 m_b; // 通用类型用于成员变量。
-
- AA() { } // 默认构造函数是空的。
- // 通用类型用于成员函数的参数。
- AA(T1 a, T2 b) :m_a(a), m_b(b) { }
- // 通用类型用于成员函数的返回值。
- T1 geta() // 获取成员m_a的值。
- {
- T1 a = 2; // 通用类型用于成员函数的代码中。
- return m_a + a;
- }
- };
-
- int main()
- {
- AA<int, string> *b = new AA<int, string>();
- }
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- template <class T1, class T2=string>
- class AA
- {
- public:
- T1 m_a; // 通用类型用于成员变量。
- T2 m_b; // 通用类型用于成员变量。
-
- AA() { } // 默认构造函数是空的。
-
- // 该成员函数并不会被调用,因此也不会报错
- T1 gethaha()
- {
- return m_a.hahaha();
- }
- // 通用类型用于成员函数的参数。
- AA(T1 a, T2 b) :m_a(a), m_b(b) { }
- };
-
- int main()
- {
- AA<int, string>* a; //在程序中,模板类的成员函数使用了才会创建。
- }
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- /
- // 类模板
- template<class T1, class T2>
- class AA { // 类模板。
- public:
- T1 m_x;
- T2 m_y;
-
- AA(const T1 x, const T2 y) :m_x(x), m_y(y) { cout << "类模板:构造函数。\n"; }
- void show() const;
- };
-
- template<class T1, class T2>
- void AA<T1, T2>::show() const { // 成员函数类外实现。
- cout << "类模板:x = " << m_x << ", y = " << m_y << endl;
- }
- /
- // 类模板完全具体化
- template<>
- class AA<int, string> {
- public:
- int m_x;
- string m_y;
-
- AA(const int x, const string y) :m_x(x), m_y(y) { cout << "完全具体化:构造函数。\n"; }
- void show() const;
- };
-
- void AA<int, string>::show() const { // 成员函数类外实现。
- cout << "完全具体化:x = " << m_x << ", y = " << m_y << endl;
- }
- /
- // 类模板部分具体化
- template<class T1>
- class AA<T1, string> {
- public:
- T1 m_x;
- string m_y;
-
- AA(const T1 x, const string y) :m_x(x), m_y(y) { cout << "部分具体化:构造函数。\n"; }
- void show() const;
- };
-
- template<class T1>
- void AA<T1, string>::show() const { // 成员函数类外实现。
- cout << "部分具体化:x = " << m_x << ", y = " << m_y << endl;
- }
- /
-
- int main()
- {
- // 具体化程度高的类优先于具体化程度低的类,具体化的类优先于没有具体化的类。
- AA<int, string> aa1(8, "我是一只傻傻鸟。"); // 将使用完全具体化的类。
- AA<char, string> aa2(8, "我是一只傻傻鸟。"); // 将使用部分具体化的类。
- AA<int, double> aa3(8, 999999); // 将使用模板类。
- }
模板类继承普通类
- #include<string>
- #include<iostream>
-
- using namespace std;
-
- // 普通类
- class AA
- {
- public:
- int m_a;
- AA(int a) :m_a(a) { cout << "调用了AA的构造函数。\n"; }
- void func1() { cout << "调用了func1()函数:m_a=" << m_a << endl;; }
- };
-
- // 模板类
- template<class T1, class T2>
- class BB:public AA
- {
- public:
- T1 m_x;
- T2 m_y;
- BB(const T1 x, const T2 y, int a) :AA(a), m_x(x), m_y(y)
- {
- cout << "调用了BB的构造函数。\n";
- }
-
- // 常函数
- void func2() const
- {
- cout << "调用了func2()函数:x = " << m_x << ", y = " << m_y << endl;
- }
- };
-
-
- int main()
- {
- BB<int, string> bb(8, "我是一只傻傻鸟", 3);
- bb.func2();
-
- }
普通类继承模板类的实例化版本
- #include<string>
- #include<iostream>
-
- using namespace std;
-
- // 模板类
- template<class T1, class T2>
- class AA
- {
- public:
- T1 m_x;
- T2 m_y;
- // 构造函数
- AA(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了AA的构造函数。\n"; }
- // 常函数
- void func1() const
- {
- cout << "调用了func1()函数:x = " << m_x << ", y = " << m_y << endl;
- }
- };
-
-
- // 普通类
- class BB:public AA<int,string>
- {
- public:
- int m_a;
- BB(int a, int x, string y) : AA(x, y), m_a(a) { cout << "调用了BB的构造函数。\n"; }
- void func2() { cout << "调用了func2()函数:m_a = " << m_a << endl;; }
- };
-
-
- int main()
- {
- BB bb(3, 8, "我是一只傻傻鸟。");
- bb.func1();
- bb.func2();
- }
- // 28行代码 AA(x, y)
普通类继承模板类
- #include<string>
- #include<iostream>
-
- using namespace std;
-
- // 模板类
- template<class T1, class T2>
- class AA
- {
- public:
- T1 m_x;
- T2 m_y;
- // 构造函数
- AA(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了AA的构造函数。\n"; }
- // 常函数
- void func1() const
- {
- cout << "调用了func1()函数:x = " << m_x << ", y = " << m_y << endl;
- }
- };
-
-
- // 普通类
- template<class T1, class T2>
- class BB :public AA<T1, T2>
- {
- public:
- int m_a;
- BB(int a, const T1 x, const T2 y) : AA<T1, T2>(x, y), m_a(a) { cout << "调用了BB的构造函数。\n"; }
- void func2() { cout << "调用了func2()函数:m_a = " << m_a << endl;; }
- };
-
-
- int main()
- {
- BB<int,string> bb(3, 8, "我是一只傻傻鸟。");
- bb.func1();
- bb.func2();
- }
-
- // 关键代码在29行, AA<T1, T2>(x, y)需要指定泛型
模板类继承模板类
- #include<string>
- #include<iostream>
-
- using namespace std;
-
- template<class T1, class T2>
- class BB
- {
- public:
- T1 m_x;
- T2 m_y;
- BB(const T1 x, const T2 y) : m_x(x), m_y(y) { cout << "调用了BB的构造函数。\n"; }
- void func2() const { cout << "调用了func2()函数:x = " << m_x << ", y = " << m_y << endl; }
- };
-
-
- template<class T, class T1, class T2>
- class CC :public BB<T1, T2> // 模板类继承模板类。
- {
- public:
- T m_a;
- CC(const T a, const T1 x, const T2 y) : BB<T1, T2>(x, y), m_a(a) { cout << "调用了CC的构造函数。\n"; }
- void func3() { cout << "调用了func3()函数:m_a = " << m_a << endl;; }
- };
-
- int main()
- {
- CC<int, int, string> cc(3, 8, "我是一只傻傻鸟。");
- cc.func3();
- cc.func2();
- }
模板类继承模板参数给出的基类
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- class AA {
- public:
- AA() { cout << "调用了AA的构造函数AA()。\n"; }
- AA(int a) { cout << "调用了AA的构造函数AA(int a)。\n"; }
- };
-
- class BB {
- public:
- BB() { cout << "调用了BB的构造函数BB()。\n"; }
- BB(int a) { cout << "调用了BB的构造函数BB(int a)。\n"; }
- };
-
- class CC {
- public:
- CC() { cout << "调用了CC的构造函数CC()。\n"; }
- CC(int a) { cout << "调用了CC的构造函数CC(int a)。\n"; }
- };
-
- template<class T>
- class DD {
- public:
- DD() { cout << "调用了DD的构造函数DD()。\n"; }
- DD(int a) { cout << "调用了DD的构造函数DD(int a)。\n"; }
- };
-
- template<class T>
- class EE : public T { // 模板类继承模板参数给出的基类。
- public:
- EE() :T() { cout << "调用了EE的构造函数EE()。\n"; }
- EE(int a) :T(a) { cout << "调用了EE的构造函数EE(int a)。\n"; }
- };
-
- int main()
- {
- EE<AA> ea1; // AA作为基类。
- EE<BB> eb1; // BB作为基类。
- EE<CC> ec1; // CC作为基类。
- EE<DD<int>> ed1; // EE<int>作为基类。
- // EE<DD> ed1; // DD作为基类,错误。
- }
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- template<class T1, class T2>
- class AA // 模板类AA。
- {
- public:
- T1 m_x;
- T2 m_y;
- AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
- void show() const { cout << "show() x = " << m_x << ", y = " << m_y << endl; }
- };
-
- // 采用普通函数,参数和返回值是模板类AA的实例化版本。
- AA<int, string> func(AA<int, string>& aa)
- {
- aa.show();
- cout << "调用了func(AA<int, string> &aa)函数。\n";
- return aa;
- }
-
- // 函数模板,参数和返回值是的模板类AA。
- template <typename T1, typename T2>
- AA<T1, T2> func(AA<T1, T2>& aa)
- {
- aa.show();
- cout << "调用了func(AA<T1, T2> &aa)函数。\n";
- return aa;
- }
-
- // 函数模板,参数和返回值是任意类型。
- template <typename T>
- T func(T& aa)
- {
- aa.show();
- cout << "调用了func(AA<T> &aa)函数。\n";
- return aa;
- }
-
- int main()
- {
- AA<int, string> aa(3, "我是一只傻傻鸟。");
- func(aa);
- }
非模板友元
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- template<class T1, class T2>
- class AA
- {
- T1 m_x;
- T2 m_y;
- public:
- AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
- // 非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数,只能在类内实现。
- friend void show(const AA<T1, T2>& a)
- {
- cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
- }
- /* friend void show(const AA<int, string>& a);
- friend void show(const AA<char, string>& a);*/
- };
-
- //void show(const AA<int, string>& a)
- //{
- // cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
- //}
- //
- //void show(const AA<char, string>& a)
- //{
- // cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
- //}
-
- int main()
- {
- AA<int, string> a(88, "我是一只傻傻鸟。");
- show(a);
-
- AA<char, string> b(88, "我是一只傻傻鸟。");
- show(b);
- }
约束模板友元
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- // 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
- template <typename T>
- void show(T& a); // 第一步:在模板类的定义前面,声明友元函数模板。
-
- template<class T1, class T2>
- class AA // 模板类AA。
- {
- friend void show<>(AA<T1, T2>& a); // 第二步:在模板类中,再次声明友元函数模板。
- T1 m_x;
- T2 m_y;
-
- public:
-
- AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
- };
-
- template<class T1, class T2>
- class BB // 模板类BB。
- {
- friend void show<>(BB<T1, T2>& a); // 第二步:在模板类中,再次声明友元函数模板。
- T1 m_x;
- T2 m_y;
-
- public:
-
- BB(const T1 x, const T2 y) : m_x(x), m_y(y) { }
- };
-
- template <typename T> // 第三步:友元函数模板的定义。
- void show(T& a)
- {
- cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
- }
-
- template <> // 第三步:具体化版本。
- void show(AA<int, string>& a)
- {
- cout << "具体AA<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
- }
-
- template <> // 第三步:具体化版本。
- void show(BB<int, string>& a)
- {
- cout << "具体BB<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
- }
-
- int main()
- {
- AA<int, string> a1(88, "我是一只傻傻鸟。");
- show(a1); // 将使用具体化的版本。
-
- AA<char, string> a2(88, "我是一只傻傻鸟。");
- show(a2); // 将使用通用的版本。
-
- BB<int, string> b1(88, "我是一只傻傻鸟。");
- show(b1); // 将使用具体化的版本。
-
- BB<char, string> b2(88, "我是一只傻傻鸟。");
- show(b2); // 将使用通用的版本。
- }
非约束模板友元
- #include <iostream> // 包含头文件。
- using namespace std; // 指定缺省的命名空间。
-
- // 非类模板约束的友元函数,实例化后,每个函数都是每个每个类的友元。
- template<class T1, class T2>
- class AA
- {
- template <typename T> friend void show(T& a); // 把函数模板设置为友元。
- T1 m_x;
- T2 m_y;
- public:
- AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
- };
-
- template <typename T> void show(T& a) // 通用的函数模板。
- {
- cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
- }
-
- template <>void show(AA<int, string>& a) // 函数模板的具体版本。
- {
- cout << "具体<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
- }
-
- int main()
- {
- AA<int, string> a(88, "我是一只傻傻鸟。");
- show(a); // 将使用具体化的版本。
-
- AA<char, string> b(88, "我是一只傻傻鸟。");
- show(b); // 将使用通用的版本。
- }
- #include<iostream>
-
- using namespace std;
-
- template<class T1, class T2>
- class AA
- {
- public:
- T1 m_x;
- T2 m_y;
- AA(const T1 x, const T2 y):m_x(x), m_y(y)
- {
- cout << "调用AA的构造函数\n";
- }
- void show()
- {
- cout << "m_x=" << m_x << ",m_y=" << m_y << endl;
- }
- template<class T>
- class BB
- {
- public:
- T m_a;
- T1 m_b;
- BB() {};
- void show();
- };
- BB<string> m_bb;
- template<typename T>
- void show(T tt);
- };
-
- // 顺序不能写反
- template<class T1, class T2>
- template<class T>
- void AA<T1, T2>::BB<T>::show()
- {
- cout << "m_a=" << m_a << ",m_b=" << m_b << endl;
- }
-
- // 顺序不能写反
- template<class T1, class T2>
- template<typename T>
- void AA<T1, T2>::show(T t)
- {
- cout << "tt=" << t << endl;
- cout << "m_x=" << m_x << ",m_y=" << m_y << endl;
- m_bb.show();
- }
- int main()
- {
- AA<int, string> a(88, "我是一只傻傻鸟。");
- a.show();
- a.m_bb.m_a = "我有一只小小鸟。";
- a.m_bb.show();
- a.show("你是一只什么鸟?");
- }
- #include<iostream>
-
- using namespace std;
-
- template<class T, int len>
- class LinkedList
- {
- public:
- T* m_head; // 链表头节点
- int m_len = len; // 链表长度
- void insert() { cout << "向链表中插入了一条记录。\n"; }
- void m_delete() { cout << "向链表中删除了一条记录。\n"; }
- void update() { cout << "向链表中更新了一条记录。\n"; }
- };
-
- template <class T, int len>
- class Array // 数组类模板
- {
- public:
- T* m_data; // 数组指针
- int m_len = len; // 数组长度
- void insert() { cout << "向数组中插入了一条记录。\n"; }
- void m_delete() { cout << "向数组中删除了一条记录。\n"; }
- void update() { cout << "向数组中更新了一条记录。\n"; }
- };
-
- //核心代码 template<class , int> class 表示模板类参数
- template<template<class , int> class table_type, class data_type, int len>
- class LinearList
- {
- public:
- table_type<data_type, len> m_table;
- void insert() { m_table.insert(); } // 线性表插入操作。
- void m_delete() { m_table.m_delete(); } // 线性表删除操作。
- void update() { m_table.update(); } // 线性表更新操作。
-
- void oper() // 按业务要求操作线性表。
- {
- cout << "len=" << m_table.m_len << endl;
- m_table.insert();
- m_table.update();
- }
- };
- int main()
- {
- // 创建线性表对象,容器类型为链表,链表的数据类型为int,表长为20。
- LinearList<LinkedList, int, 20> a;
- a.insert();
- a.m_delete();
- a.update();
-
- // 创建线性表对象,容器类型为数组,数组的数据类型为string,表长为20。
- LinearList<Array, string, 20> b;
- b.insert();
- b.m_delete();
- b.update();
-
- }
用于内置数据类型之间的转换
- #include <iostream>
- using namespace std;
-
- int main(int argc, char* argv[])
- {
- int ii = 3;
- long ll = ii; // 绝对安全,可以隐式转换,不会出现警告。
-
- double dd = 1.23;
- long ll1 = dd; // 可以隐式转换,但是,会出现可能丢失数据的警告。
- long ll2 = (long)dd; // C风格:显式转换,不会出现警告。
- long ll3 = static_cast<long>(dd); // C++风格:显式转换,不会出现警告。
- cout << "ll1=" << ll1 << ",ll2=" << ll2 << ",ll3=" << ll3 << endl;
- }
用于指针之间的转换
- #include <iostream>
- using namespace std;
-
- void func(void* ptr) { // 其它类型指针 -> void *指针 -> 其它类型指针
- double* pp = static_cast<double*>(ptr);
- }
-
- int main(int argc, char* argv[])
- {
- int ii = 10;
-
- //double* pd1 = ⅈ // 错误,不能隐式转换。
- double* pd2 = (double*) ⅈ // C风格,强制转换。
- //double* pd3 = static_cast<double*>(&ii); // 错误,static_cast不支持不同类型指针的转换。
-
- void* pv = ⅈ // 任何类型的指针都可以隐式转换成void*。
- double* pd4 = static_cast<double*>(pv); // static_cast可以把void *转换成其它类型的指针。
- func(&ii);
- }
类似c风格的强制转换
- int* p = nullptr;
- double* b = reinterpret_cast<double*>(p); //正确,但是有风险
主要用在继承结构中,可以支持RTTI类型识别的上下转换
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。