封装的多层含义
案例:设计⽴⽅体类(cube),求出⽴⽅体的⾯积和体积。求两个⽴⽅体,是否相等(全局函数和成员函数都要实现)。
- #include
-
- class Cube
- {
- public:
- void setA(int a){m_a = a;}
- void setB(int b){m_b = b;}
- void setC(int c){m_c = c;}
- void setABC(int a = 0, int b = 0, int c = 0)
- {
- m_a = a;
- m_b = b;
- m_c = c;
- }
- int getA(){return m_a;}
- int getB(){return m_b;}
- int getC(){return m_c;}
- public:
- int getV()
- {
- m_v = m_a * m_b * m_c;
- return m_v;
- }
- int getS()
- {
- m_s = 2 * (m_a * m_b + m_a * m_c + m_b * m_c);
- return m_s;
- }
- int judgeCube(Cube &v1, Cube &v2) //不建议这样写
- {
- if ((v1.getA() == v2.getA())
- && (v1.getB() == v2.getB())
- && (v1.getC() == v2.getC()))
- return 1;
- return 0;
- }
- int judgeCube(Cube &v2) //成员函数 函数重载
- {
- if ((m_a == v2.getA())
- && (m_b == v2.getB())
- && (m_c == v2.getC()))
- return 1;
- return 0;
- }
- private:
- int m_a;
- int m_b;
- int m_c;
- int m_v;
- int m_s;
- };
-
- // 全局函数 PK 成员函数
- // 1相等 0不相等
- int judgeCube(Cube &v1, Cube &v2)
- {
- if ((v1.getA() == v2.getA())
- && (v1.getB() == v2.getB())
- && (v1.getC() == v2.getC()))
- return 1;
- return 0;
- }
-
- int main()
- {
- Cube v1, v2;
- v1.setABC(1, 2, 3);
- std::cout << v1.getS() << std::endl;
- std::cout << v1.getV() << std::endl;
-
-
- // 用成员函数判断两个立方体是否相等
- v2.setABC(1, 2, 4);
- if (v1.judgeCube(v1, v2) == 1)
- std::cout << "v1 = v2" << std::endl;
- else
- std::cout << "v1 != v2" << std::endl;
-
- // 用成员函数判断两个立方体是否相等
- v1.setABC(1, 2, 4);
- if (v1.judgeCube(v2) == 1)
- std::cout << "v1 = v2" << std::endl;
- else
- std::cout << "v1 != v2" << std::endl;
-
- // 用全局函数判断两个立方体是否相等
- if (judgeCube(v1, v2) == 1)
- std::cout << "v1 = v2" << std::endl;
- else
- std::cout << "v1 != v2" << std::endl;
-
- return 0;
- }
-
设计.h和.cpp,一个声明,一个实现。为了避免同⼀个头⽂件被include多次带来重复编译,头⽂件要么用#ifndef 宏名 #define 宏名 #endif编写头文件,要么用#pragma once。#ifndef由语⾔⽀持移植性更好,推荐使用。
案例:带有数组成员的类
myarrray.h
- #ifndef _MYARRAY_H_
- #define _MYARRAY_H_
-
- class Array
- {
- public:
- Array(int length);
- Array(const Array &obj); //加了const更好
- ~Array();
- public:
- void setdata(int index, int value);
- int getdata(int index);
- int getLenth();
- private:
- int m_length;
- int *m_space;
- };
-
- #endif
myarray.cpp
- #include
- #include "myarray.h"
-
- //作用域不要忘写
- //构造函数
- Array::Array(int length)
- {
- if (length < 0){length = 0;}
- m_length = length;
- m_space = new int[length];
- // this->m_length = length;
- // this->m_space = new int[length];
- }
- //构造函数的重载
- Array::Array(const Array &obj)
- {
- m_length = obj.m_length;
- m_space = new int[m_length];
- for (int i = 0; i < m_length; i++)
- m_space[i] = obj.m_space[i];
- }
- //析构函数
- Array::~Array()
- {
- if (m_space != NULL)
- {
- delete[] m_space;
- m_space = NULL;
- m_length = -1;
- }
- }
- //成员函数
- void Array::setdata(int index, int value)
- {
- if (0 <= index && index <= m_length)
- m_space[index] = value;
- }
- int Array::getdata(int index)
- {
- if (0 <= index && index <= m_length)
- return m_space[index];
- return -1;
- }
- int Array::getLenth()
- {
- return m_length;
- }
main.cpp
- #include
- #include "myarray.h"
-
- int main()
- {
- Array array1(4);
- for (int i = 0; i < array1.getLenth(); i++)
- {
- array1.setdata(i, i + 100);
- std::cout << array1.getdata(i) << "\t";
- }
- std::cout << std::endl;
-
- Array array2 = array1;
- for (int i = 0; i < array2.getLenth(); i++)
- {
- std::cout << array2.getdata(i) << "\t";
- }
- std::cout << std::endl;
-
- return 0;
- }
编译运行
- [root@centos1 test]# g++ -o main main.cpp myarray.cpp
- [root@centos1 test]# ./main
- 100 101 102 103
- 100 101 102 103
- [root@centos1 test]#
1 构造函数定义
2 构造函数的调⽤
3 析构函数定义及调⽤
⼆个特殊的构造函数:
示例代码
- #include
- #include
-
- class Test
- {
- public:
- Test() //无参数构造函数
- {
- age = 0;
- strcpy(name, "tom");
- }
- Test(int age) //有参数构造函数 //构造函数重载
- {
- this->age = age;
- strcpy(name, "tom");
- }
- Test(int age, char *name) //有参数构造函数 //构造函数重载
- {
- this->age = age;
- strcpy(this->name, name);
- }
- Test(const Test &obj) // 拷贝构造函数
- {
- this->age = obj.age;
- this->name = obj.name;
- }
- ~Test() //析构函数
- {
- std::cout << "object destroyed" << std::endl;
- }
- public:
- void printT()
- {
- std::cout << "name = " << name << std::endl;
- std::cout << "age = " << age << std::endl;
- std::cout << "---------------" << std::endl;
- }
- private:
- char *name = new char[20];
- int age;
- };
-
-
-
- int main()
- {
- // 1.c++编译器自动的调用构造函数
- Test t0; // 自动调用无参数构造函数
- t0.printT();
- char name1[] = "hello";
- Test t1(10, name1); // 自动调用参数构造函数
- t1.printT();
-
- // 2.手动调用构造函数
- char name2[] = "world";
- Test t2 = Test(100, name2);
- t2.printT();
- strcpy(name2, "good");
- Test t3 = Test(1000, name2); //t4对象的初始化
- t2 = t3;
- t2.printT();
-
- // 3.对象赋值
- t0 = t1;
- std::cout << &t0 << "," << &t1 << std::endl; // 不同
- t0.printT();
- //对象的初始化 和 对象的赋值 是两个不同的概念
-
- // 4.拷贝构造函数
- Test t4 = Test(t3);
- //Test t4 = t3; // 等价的方式 // 对象的初始化
- t4.printT();
- std::cout << &t3 << "," << &t4 << std::endl; // 不同
-
- return 0;
- }
-
编译运行
- [root@centos1 test]# g++ main.cpp -o main -std=c++11
- [root@centos1 test]# ./main
- name = tom
- age = 0
- ---------------
- name = hello
- age = 10
- ---------------
- name = world
- age = 100
- ---------------
- name = good
- age = 1000
- ---------------
- 0x7ffd41eaa4e0,0x7ffd41eaa4c0
- name = hello
- age = 10
- ---------------
- name = good
- age = 1000
- ---------------
- 0x7ffd41eaa490,0x7ffd41eaa480
- object destroyed
- object destroyed
- object destroyed
- object destroyed
- object destroyed
- [root@centos1 test]#
可以用函数返回对象,为匿名对象。关于匿名对象的去和留,结论如下:
示例代码
- #include
-
- class Location
- {
- public:
- Location(int xx = 0, int yy = 0)
- {
- X = xx;
- Y = yy;
- std::cout << "Object Created\n";
- }
- Location(const Location &p) //拷贝构造函数
- {
- X = p.X;
- Y = p.Y;
- std::cout << "Object Copy" << std::endl;
- }
- ~Location()
- {
- std::cout << "(" << X << "," << Y << ") "
- << "Object Destroyed" << std::endl;
- }
- public:
- int GetX() { return X; }
- int GetY() { return Y; }
- private:
- int X, Y;
- };
-
- void PrintLocation(Location p) //打印对象的属性
- {
- std::cout << "Funtion:" << p.GetX() << "," << p.GetY() << std::endl;
- }
-
- Location GenLocation() //返回一个匿名对象
- {
- Location A(1, 2);
- return A;
- }
-
- // 匿名对象的去和留,关键看,返回时如何接盘
- // = 在赋值和初始化这两种情况时有所不同
- int main()
- {
- // (1)若返回的匿名对象,赋值给另外一个同类型的对象,那么匿名对象会被析构
- std::cout << "--------B--------" << std::endl;
- Location B; // 调用一次构造函数
- B = GenLocation(); // 调用构造函数 // 然后调用析构函数
-
-
- //(2)若返回的匿名对象,直接来初始化另外一个同类型的对象
- // 那么匿名对象会直接转成新的对象
- std::cout << "--------C--------" << std::endl;
- Location C = GenLocation(); // 调用一次构造函数
-
-
- //(3)没人接盘 //这个匿名对象构造后会直接析构
- std::cout << "----------------" << std::endl;
- GenLocation(); // 调用一次构造函数 // 然后调用析构函数
-
-
- std::cout << "--------end--------" << std::endl;
- // 程序结束时
- // B调用一次析构函数
- // C调用一次析构函数
-
- return 0;
- }
-
编译运行
- [root@localhost test]# g++ main.cpp -o main
- [root@localhost test]# ./main
- --------B--------
- Object Created
- Object Created
- (1,2) Object Destroyed
- --------C--------
- Object Created
- ----------------
- Object Created
- (1,2) Object Destroyed
- --------end--------
- (1,2) Object Destroyed
- (1,2) Object Destroyed
- [root@localhost test]#
情况描述:如果我们设计的类中有一个成员,它本身是另一个类,而且这个成员构造函数只有带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
解决办法:C++在构造函数中提供初始化列表对成员变量进行初始化。
语法规则:作用域::类名(参数1, 参数2, ...):成员类名1(实参值a, 实参值b,...), 成员类名2((实参值m, 实参值n,...)
构造函数和析构函数调用顺序:
示例:在B类中,组合了一个 A类对象,A类设计了构造函数,但构造函数只有带参的构造函数,根据构造函数的调用规则,必须要调用A类的带参构造函数。
示例代码
- #include
- #include
-
- class A
- {
- public:
- A(int _a)
- {
- a = _a;
- std::cout << "generate A " << a << std::endl;
- }
- ~A()
- {
- std::cout << "destroy A " << a << std::endl;
- }
- protected:
- private:
- int a;
- };
-
- class B
- {
- public:
- //B(int _b1, int _b2): a1(_b1), a2(_b2)
- B(int _b1, int _b2): a1(1), a2(2)
- {
- b1 = _b1;
- b2 = _b2;
- std::cout << "generate B " << std::endl;
- }
- B(int _b1, int _b2, int m, int n):a1(m), a2(n)
- {
- b1 = _b1;
- b2 = _b2;
- std::cout << "generate B " << std::endl;
- }
- ~B()
- {
- std::cout << "destroy B " << std::endl;
- }
- protected:
- private:
- int b1;
- int b2;
- A a2;
- A a1;
- };
-
- void objplay()
- {
- B(1, 2);
- }
-
- int main()
- {
- objplay();
- return 0;
- }
运行结果
- generate A 2
- generate A 1
- generate B
- destroy B
- destroy A 1
- destroy A 2
当类成员中含有⼀个const对象时,或者是⼀个引⽤时,他们也必须要通过成员初始化列表进⾏初始化,因为这两种对象要在声明后⻢上初始化,⽽在构造函数中,做的是对他们的赋值,这样是不被允许的。
示例代码
- #include
- #include
-
- class A
- {
- public:
- A(int _a)
- {
- a = _a;
- std::cout << "generate A " << a << std::endl;
- }
- ~A()
- {
- std::cout << "destroy A " << a << std::endl;
- }
- public:
- int geta(){return a;}
- protected:
- private:
- int a;
- };
-
- class B
- {
- public:
- B(int _b1, int _b2): a1(_b1), a2(_b2), c(_b2)
- // B(int _b1, int _b2): a1(1), a2(2), c(0)
- {
- b1 = _b1;
- b2 = _b2;
- std::cout << "generate B " << std::endl;
- }
- B(int _b1, int _b2, int m, int n):a1(m), a2(n), c(0)
- {
- b1 = _b1;
- b2 = _b2;
- std::cout << "generate B " << std::endl;
- }
- ~B()
- {
- std::cout << "destroy B " << std::endl;
- }
- public:
- void print()
- {
- std::cout << "---------" << std::endl;
- std::cout << b1 << std::endl;
- std::cout << b2 << std::endl;
- std::cout << a2.geta() << std::endl;
- std::cout << a1.geta() << std::endl;
- std::cout << c << std::endl;
- }
- protected:
- private:
- int b1;
- int b2;
- A a2;
- A a1;
- const int c;
- };
-
- void objplay()
- {
- B obj_b1 = B(1, 2);
- obj_b1.print();
-
- B obj_b2(3, 4);
- obj_b2.print();
- }
-
- int main()
- {
- objplay();
- return 0;
- }
-
运行结果
- generate A 2
- generate A 1
- generate B
- ---------
- 1
- 2
- 2
- 1
- 2
- generate A 4
- generate A 3
- generate B
- ---------
- 3
- 4
- 4
- 3
- 4
- destroy B
- destroy A 3
- destroy A 4
- destroy B
- destroy A 1
- destroy A 2
在软件开发过程中,常常需要动态地分配和撤销内存空间。在C语⾔中是利⽤库函数malloc和 free来分配和撤销内存空间的。C++提供了较简便⽽功能较强的运算符new和delete来取代 malloc和free函数。
注意:new和delete是运算符,不是函数,因此执⾏效率⾼。
虽然为了与C语⾔兼容,C++仍保留malloc和free函数,但建议⽤户不⽤malloc和free函数,⽽ ⽤new和delete运算符。
new用法举例:
注意⽤new分配数组空间时不能指定初值。如果由于内存不⾜等原因⽽⽆法正常分配空间,则new会 返回⼀个空指针NULL,⽤户可以根据该指针的值判断分配空间是否成功。
delete用法:
若P是⼀个指向数组的指针,则delete p表示清空内存⾸地址,delete[] p表示清空整个数组 对应的堆中内存空间。
malloc-free和new-delete区别:
示例代码
- #include
- #include
-
- class Test
- {
- public:
- Test(int _a)
- {
- a = _a;
- std::cout << "generate A" << std::endl;
- }
- ~Test()
- {
- std::cout << "destroy A" << std::endl;
- }
- protected:
- private:
- int a;
- };
-
- int main()
- {
- // 1.基础类型
- // 用malloc这个函数
- int *p1 = (int *)malloc(sizeof(int));
- *p1 = 10;
- free(p1);
-
- // 用new这个运算符
- int *p2 = new int;
- *p2 = 20;
- free(p2);
-
- int *p3 = new int(30);
- delete p3;
-
- // 2.数组
- // malloc
- int *p4 = (int *)malloc(sizeof(int) * 10); // int array[10];
- p4[0] = 1;
- free(p4);
-
- // new
- int *pArray = new int[10];
- pArray[1] = 2;
- delete[] pArray;
-
- char *pArray2 = new char[25];
- delete[] pArray2;
-
- // 3.给对象分配堆内内存空间
- std::cout << "---------malloc------------" << std::endl;
- Test *testp1 = (Test *)malloc(sizeof(Test));
- std::cout << "---------free------------" << std::endl;
- free(testp1);
-
- std::cout << "----------new-----------" << std::endl;
- Test *testp2 = new Test(1);
- std::cout << "----------delete-----------" << std::endl;
- delete testp2;
-
- return 0;
- }
运行结果
- ---------malloc------------
- ---------free------------
- ----------new-----------
- generate A
- ----------delete-----------
- destroy A
思考:每个变量,拥有属性。有没有⼀些属性,归所有对象拥有?
把⼀个类的成员修饰为 static 时,这个类⽆论有多少个对象被创建,这些同类对象共享这个 static 成员。
- #include
-
- class counter
- {
- public:
- static int num; //声明静态数据成员
- public:
- void setnum(int i) { num = i; } //成员函数访问静态数据成员
- void shownum() { std::cout << num << std::endl; }
- };
-
- // 在类的外部初始化静态数据成员
- // 只能初始化一次
- int counter::num = 0;
-
- int main()
- {
- counter a, b;
-
- //调用成员函数访问私有静态数据成员
- a.shownum(); //0
- b.shownum(); //0
-
- //只要有一个对象改变了静态成员的值
- //则所有对象调用该静态成员时的值也会变
- a.setnum(10);
- a.shownum(); //10
- b.shownum(); //10
-
- //若静态数据成员是public的
- //还可以直接通过类访问静态成员
- std::cout << counter::num << std::endl;
-
- return 0;
- }
用static修饰成员函数,成为静态函数。
对于静态成员函数,是全部对象公有函数,提供不依赖于类数据结构的共同操作,它没有this指针。
注意静态函数中,不能使用普通成员变量,也不能调用普通成员函数,只能使用静态成员变量
- #include
-
- class Worker
- {
- public:
- void company_age_add(){company_age++;}
- void print_company_age1()
- {
- std::cout << "normal print: company_age=" << company_age << std::endl;
- }
- static void print_company_age2() //静态成员函数
- {
- std::cout << "static print: company_age=" << company_age << std::endl;
-
- // std::cout << "age:" << age << std::endl;
- // error C2597: 对非静态成员“Worker::age”的非法引用
- // 静态函数中 不能使用 普通成员变量 普通成员函数
- }
- protected:
- private:
- int age;
- int salary;
- static int company_age; //静态成员变量
- };
-
-
- int Worker::company_age = 10;
-
- int main()
- {
- Worker w1, w2, w3;
- // 1.普通成员函数调用静态成员变量
- w1.print_company_age1(); // normal print: company_age=10
-
- w2.company_age_add();
- w3.print_company_age1(); // normal print: company_age=11
-
- // 2.调用静态成员函数
- // 第一种:用对象.
- w3.print_company_age2(); // static print: company_age=11
- // 第二种:类::
- Worker::print_company_age2(); // static print: company_age=11
-
- return 0;
- }
C++类对象中的成员变量和成员函数是分开存储的。
- #include
- #include
-
- class C1
- {
- public:
- int i; // 4个字节
- int j; // 4个字节
- int k; // 4个字节
- protected:
- private:
- }; //共12个字节
-
- class C2
- {
- public:
- int i;
- int j;
- int k;
- static int m;
- public:
- int getK() const { return k; }
- void setK(int val) { k = val; }
- protected:
- private:
- };
-
- struct S1
- {
- int i;
- int j;
- int k;
- }; // 12
-
- struct S2
- {
- int i;
- int j;
- int k;
- static int m;
- }; // 12
-
-
- int main()
- {
- printf("c1:%d \n", sizeof(C1)); //12
- printf("c2:%d \n", sizeof(C2)); //12
- printf("s1:%d \n", sizeof(S1)); //12
- printf("s2:%d \n", sizeof(S2)); //12
- return 0;
- }
C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。
静态成员函数与普通成员函数的区别:静态成员函数不包含指向具体对象的指针,普通成员函数 包含⼀个指向具体对象的指针。
- #include
-
- class Test
- {
- public:
- Test(int a, int b)
- {
- this->a = a;
- this->b = b;
- }
-
- void print1(int a) //这个a是函数形参的a
- {
- std::cout << "----------print1----------" << std::endl;
- for (int i = 0; i < a; i++)
- {
- std::cout << "a: " << this->a << std::endl; //打印对象的属性a的值
- std::cout << "b: " << this->b << std::endl;
- }
- }
-
- void print2()
- {
- std::cout << "----------print2----------" << std::endl;
- // 当函数形参没有与成员变量同名的形参时
- // 函数内部可以不用this,直接访问成员变量
- std::cout << "a: " << a << std::endl; //打印对象的属性a的值
- std::cout << "b: " << b << std::endl;
-
- }
-
- protected:
- private:
- int a;
- int b;
- };
-
- int main()
- {
- Test t1(1, 2);
- t1.print1(3);
-
- t1.print2();
-
- return 0;
- }
C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。
将const写在成员函数处,比如void OperateVar(int a, int b) const,将会对this指针和this下的成员进行修饰,只有只读属性,使得this和this成员不能被改变。
注意:
void OperateVar(int a, int b) const
const void OperateVar(int a, int b)
void const OperateVar(int a, int b)
const修饰的是谁?
const修饰的是形参a吗?不是
const修饰的是属性this->a this->b
const修饰的是this指针所指向的内存空间, 修饰的是this指针
- #include
-
- class Test
- {
- public:
- Test(int a, int b) //---> Test(Test *this, int a, int b)
- {
- this->a = a;
- this->b = b;
- }
- void printT()
- {
- std::cout << "a: " << a << "\t";
- std::cout << "b: " << this->b << std::endl;
- }
-
- void OperateVar(int a, int b) const
- // void const OperateVar(int a, int b)
- // const void OperateVar(int a, int b)
- {
- a = 100;
- // this->a = 100; //因为const,会报错
- // this->b = 200; //因为const,会报错
- // this = 0x11; //因为const,会报错
- std::cout << "a: " << this->a << "\t";
- std::cout << "b: " << this->b << std::endl;
- }
- protected:
- private:
- int a;
- int b;
- };
-
- int main()
- {
- int *m_space = new int[0];
- if (m_space == NULL)
- {
- return -1;
- }
-
- Test t1(1, 2);
- t1.printT();
- //a: 1 b: 2
-
- t1.OperateVar(100, 200);
- //a: 1 b: 2
-
- return 0;
- }
end