类的设计:可以把属性和行为放在不同的权限下
struct和class区别在于某人的访问权限不同
如果我们不写,系统会自己给我没写
没有返回值,也不写void
函数名和类型相同
可以有参,也可以无参
在调用对象会自动调用函数,无需手动调用,只调用一次
同上,不过是无参(不可以重载),销毁时自动调用函数,只调用一次
分类
函数名(const 函数名 &p(对象)){ age = p.age; }
#include<iostream> //构造函数和析构函数 using namespace std; class person{ public: int age; public: //构造函数 //普通 person(){ cout << "Person无参构造函数的调用" << endl; } person(int a){ age = a; cout << "Person有参构造函数的调用" << endl; } //拷贝 person(const person &p){ age = p.age; cout << "Person拷贝构造函数的调用" << endl; } //析构函数 ~person(){ cout << "Person析构函数的调用" << endl; } }; void test01(){ //调用 //1.括号法 //person p1; //person p2(10); //person p3(p2); //注:调用默认构造函数时,不要加()(系统会以为是一个函数的声明) //cout << "p2的年龄:" << p2.age << endl; //cout << "p3的年龄:" << p3.age << endl; //2.显示法 person p4; person p5 = person(10);//右值:匿名对象,当前行结束,系统收回匿名对象 person p6 = person(p5); //注:不要利用拷贝函数初始化匿名对象 //3.隐式转换法 person p7 = 10;//相当于person p5 = person(10); person p8 = p7; } int main(){ test01(); return 0; }*/
c++中调用拷贝函数一般三种请况
#include<iostream> //拷贝时机 using namespace std; class Person{ private: int m_Age; public: Person(){ cout << "person的默认无参构造函数调用" << endl; } Person(int age){ cout << "Person有参构造函数调用" << endl; m_Age = age; } Person(const Person &p){ cout << "Person的拷贝调用" << endl; m_Age =p.m_Age; } ~Person(){ cout << "person的析构函数调用" << endl; } }; void test01(){ Person p1 = Person(20); Person p2(p1); } void dowork(Person p){ } void test02(){ Person p; dowork(p); } Person dowork2(){ Person p1; cout << (int*)&p1 << endl; //输出地址 return p1; } void test03(){ Person p = dowork2(); cout << (int*)&p << endl; } int main(){ test01(); cout << endl; test02(); cout << endl; test03(); return 0; }
默认情况下,C++会至少给一个类添加3个函数
规则如下:
注意:若只定义了拷贝(只有参同理),则
Person p1; //和 Person p1(20); //均是错误的(因为系统不会提供)
浅拷贝:简单的赋值拷贝操作(如果对其进行释放,则堆区的内存会重复释放,出现错误)
深拷贝:在堆区重新申请空间,进行拷贝操作
m_height = new int(*P.m_height); //new返回的值是地址 private: int * m_height; //地址(指针)类型
作用:用来初始化属性
语法:
构造函数():属性1(值1),属性2(值2)...{}
来个浅例吧
#include <iostream> using namespace std; class Person{ public: //传统初始化 // Person(int a,int b,int c){ // m_A = a; // m_B = b; // m_C = c; // } //初始化列表 Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){} //参数a,b,c可以自由的改变所赋的值 int m_A; int m_B; int m_C; }; void test01(){ Person p(30,20,10); //Person p; cout << "m_A:" << p.m_A << endl; cout << "m_B:" << p.m_B << endl; cout << "m_C:" << p.m_C << endl; } int main(int argc, char** argv) { test01(); return 0; }
结果是30 20 10
C++类中的成员可以是另一个类的对象,我们成该成员为对象成员
例如:
class A{}; class B{ A a; } //敲黑板(好老的梗...) //先构造A的对象(即先构造其他类的对象),再构造B的对象 //析构的顺序是相反的,先析构本类,再析构其他类
代码的简单例子
#include <iostream> using namespace std; #include<cstring> //要用字符串呢 class Phone{ //类一 public: //品牌名字 string m_Pname; Phone(string name){ m_Pname = name; } }; class Person{ //类二 public: //姓名 string m_Name; //手机 Phone m_Phone; //类一作为类二的成员 Person(string Name,string Pname):m_Name(Name),m_Phone(Pname){} //相当于Phone m_Phone = Pname = Phone(Pname)(隐式转化) }; void test01(){ Person p("张三","华为"); cout << p.m_Phone.m_Pname; } int main(int argc, char** argv) { test01(); return 0; }
结果:华为
静态成员变量就是加上const
静态成员分成:
#include <iostream> using namespace std; class Person{ public: //静态成员函数 void static func(){ m_a = 100;//静态成员函数访问静态成员变量 cout << "func的调用" << m_a << endl; } static int m_a;//类内声明类外初始化 }; int Person::m_a = 0; //两种访问方式 void test01(){ //通过对象访问 Person p; p.func(); //通过类名访问 Person::func(); } int main(int argc, char** argv) { test01(); return 0; }
结果
只有非静态成员变量才属于类的对象上面
空对象占用一个字节
C++编译器会给每个空对象分配一个字节的空间(独一无二的内存地址),防止区分空对象占内存的位置
#include <iostream> using namespace std; class Person1{}; class Person2{ int m_a;//非静态成员变量,属于类的对象上 static int m_b;//静态成员变量,不属于类的对象上 void test01(){}//非静态成员函数,不属于类的对象上 static void test02(){}//静态成员函数,不属于类的对象上 }; int Person2::m_b = 0; void test01(){//空对象所占用的内存 Person1 p1; cout << "p1 sizeof of p is " << sizeof(p1) << endl; } void test02(){//非空对象占用的内存 Person2 p2; cout << "p2 sizeof of p is " << sizeof(p2) << endl; } int main(int argc, char** argv) { test01(); test02(); return 0; }
引子:在上面我们知道,非静态的成员函数只会生成一份函数实例,也是是说多个同类的对象会公用一块代码(一个函数),那么:这一块代码是如何区分是那个对象调用自己呢?
通过this指针来解决上面的问题,this指针指向被调用的成员函数所属的对象(eg:p1调用就指向p1...)
用途
#include <iostream> using namespace std; class Person1{//名称冲突 public: Person1(int age,int age1){ this->age = age; age1 = age1; } int age; int age1; Person1 & Add(Person1 &p){ this->age += p.age; //this是一个指向p3的指针,*this就是对象p3的本体 return *this; } }; void test01(){ Person1 p1(18,18); cout << "p1的年龄是" << p1.age << endl; cout << "p1的年龄是" << p1.age1 << endl; } void test02(){//把p2的年龄加到p3上 Person1 p2(10,10); Person1 p3(10,10); p3.Add(p2).Add(p2);//链式编程思想 cout << "p3的年龄是" << p3.age << endl; } int main(int argc, char** argv) { test01(); test02(); return 0; }
结果
C++中允许空指针调用成员函数的,但是也要注意有没有用到this地址
如果用到this指针,则需要加以判断确保代码的健壮性
if(this == NULL) return;
看个小例子吧
#include <iostream> using namespace std; class Person{ public: void show(){ cout << "show的调用" << endl; } int m_age; void get(){ if(this == NULL) return; cout << "age=" << m_age << endl; //默认this->m_age } }; void test01(){ Person * p = NULL; //空指针可以访问成员 p->show(); p->get(); } int main(int argc, char** argv) { test01(); return 0; }
常函数
class Person{ public: //this本质 指针常量 Person * const this 指向不可以改变 //在成员函数后面+const <=>const Person * const this,让指针指向的值不可以改变 void show() const{ m_a =100;//所以会报错哦 //其实是this->m_a = 100; } int m_a; };
常对象
const Person p; p.show();
在程序里,有些私有的属性想让类外的特殊的一些函数或者类进行调用,就需要友元技术
作用(目的):让一个函数或是类访问另一个类中的私有成员
友元关键字:friend(友元:不是类的成员,不受访问限制)
友元的三种实现
#include <iostream> using namespace std; #include<cstring> class Building{ //goodFriend是Building类的好朋友,可以访问啦 friend void goodFriend(Building &building); public: Building(){ SittingRoom = "客厅"; BedRoom = "卧室"; } public: string SittingRoom;//客厅 private: string BedRoom;//卧室 }; //全局函数 void goodFriend(Building &building){ cout << "友元全局函数 正在访问:" << building.SittingRoom << endl; cout << "友元全局函数 正在访问:" << building.BedRoom << endl; } void test01(){ Building building; goodFriend(building); } int main(int argc, char** argv) { test01(); return 0; }
结果
大致流程:
#include <iostream> using namespace std; #include<cstring> class Building{ //GoodFriend类是Building类的好朋友 friend class GoodFriend; ...//和上面一样 }; class GoodFriend{ public: GoodFriend(){ //创建对象 building = new Building; } void visit(){//参观函数 访问Building中的属性 cout << "友元正在访问:" << building->SittingRoom << endl; cout << "友元正在访问:" << building->BedRoom << endl; } Building * building; }; void test01(){ GoodFriend GF; GF.visit(); } int main(int argc, char** argv) { test01(); return 0; }
结果
流程与上面的几乎一样
#include <iostream> using namespace std; #include<cstring> class Building;//防止在未创建BUilding类是报错 class GoodFriend{ public: Building * building; GoodFriend(); void visit();//参观函数 访问Building中的私有成员 }; class Building{ //visit()做为BUilding类的好朋友 friend void GoodFriend::visit(); public: string SittingRoom;//客厅 private: string BedRoom;//卧室 public: Building(); }; //类外声明 Building::Building(){ SittingRoom = "客厅"; BedRoom = "卧室"; } GoodFriend::GoodFriend(){ building = new Building; } void GoodFriend::visit(){//参观函数 访问Building中的私有成员 cout << "友元正在访问:" << building->SittingRoom << endl; cout << "友元正在访问:" << building->BedRoom << endl; } void test01(){ //测试函数 GoodFriend GF; GF.visit(); } int main(int argc, char** argv) { test01(); return 0; }
结果:
概念:对已有运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
对于内置的数据类型,系统知道如何进行运算
本质:Person p3 = p1.operator+(p2);
#include <iostream> using namespace std; class Person{ public: int m_A; int m_B; /*======================================================*/ Person operator+(Person &p){ Person temp; temp.m_A = this->m_A + p.m_A; temp.m_B = this->m_B + p.m_B; return temp; } /*======================================================*/ }; void test01(){ 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 <<endl; cout << p3.m_B <<endl; } int main(int argc, char** argv) { test01(); return 0; }
结果是两个20(相加成功)
本质:Person p3 = operator+(p1,p2);
#include <iostream> using namespace std; class Person{ public: int m_A; int m_B; }; /*======================================================*/ 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; } /*======================================================*/ int main(int argc, char** argv) { //函数和上面的一样 test01(); return 0; }
结果也是两个20
注意:- 运算符的重载也可以发生函数重载(名字相同,参数不同)
- 不可以改变内置运算符
只能利用全局函数重载左移运算符
作用:输出自定义的数据类型
本质:operator<<(cout,p) => cout << p
#include <iostream> using namespace std; class Person{ public: int m_A; int m_B; }; /*======================================================*/ ostream & operator<<(ostream &out,Person &p){ out << "m_A:" << p.m_A << '\t' << "m_B:" << p.m_B << endl; return out; } /*======================================================*/ void test01(){ Person p; p.m_A = 10; p.m_B = 10; cout << p << endl; } int main(int argc, char** argv) { test01(); return 0; }
结果:m_A:10 m_B:10
若类的成员变成私有:用友元
class Person{ friend ostream & operator<<(ostream &out,Person &p); public: int m_A; int m_B; };
前置返回引用,后置返回值
#include <iostream> using namespace std; class MyInteger{//自定义的整型 friend ostream & operator<<(ostream &out,MyInteger &p); public: MyInteger(){ m_Num = 0; } /*======================================================*/ MyInteger & operator++(){//返回引用是为了一直对一个数据操作 m_Num++; return *this; } /*======================================================*/ private: int m_Num; }; ostream & operator<<(ostream &out,MyInteger &p){ out << p.m_Num; return out; } void test01(){ MyInteger myint; cout << "myint:" << ++(++myint) << endl; cout << "myint:" << myint << endl; } int main(int argc, char** argv) { test01(); return 0; }
结果1:myint:2(换行)myint:2
#include <iostream> using namespace std; class MyInteger{//自定义的整型 friend ostream & operator<<(ostream &out,const MyInteger &p); public: MyInteger(){ m_Num = 0; } /*======================================================*/ MyInteger operator++(int){//int 代表占位参数,可以用于区分前置和后置递增 //先记录 MyInteger temp = *this; //后递增 m_Num++; //再返回 return temp; } /*======================================================*/ private: int m_Num; }; ostream & operator<<(ostream &out, const MyInteger &p){//这里加了const,否则在test02()的输出会有问题 out << p.m_Num; return out; } void test02(){ MyInteger myint; cout << "myint:" << myint++ << endl; cout << "myint:" << myint << endl; } int main(int argc, char** argv) { test02(); return 0; }
结果2:myint:0(换行)myint:1
补充构造函数调用规则,一个类至少4个函数
p2 = p1的问题:堆区重复释放,和浅拷贝的问题是一样的
#include <iostream> using namespace std; class Person{ public: int *m_age;//开辟到堆区 Person(int age){ m_age = new int(age); } ~Person(){ if(m_age != NULL){ delete m_age; m_age = NULL; } } /*======================================================*/ Person & operator=(Person &p){//和深拷贝几乎是一样的,返回值是引用是要满足连等 //先判断左值是否有属性在堆区,如果有,先释放干净,再深拷贝 if(m_age != NULL){ delete m_age; m_age = NULL; } m_age = new int(*p.m_age); //返回对象本身 return *this; } }; /*======================================================*/ void test01(){ 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; } int main(int argc, char** argv) { test01(); return 0; }
结果:p1,p2,p3都是10
#include <iostream> using namespace std; class Person{ public: string m_name; int m_age; Person(string name,int age){ m_name = name; m_age = age; } ~Person(){} /*======================================================*/ bool operator==(Person &p){ if(this->m_name == p.m_name&&this->m_age == p.m_age){ return true; }else{ return false; } } /*======================================================*/ }; void test01(){ Person p1("Tom",18); Person p2("Tom",18); if(p1 == p2) cout << "p1和p2相等" << endl; else cout << "p1和p2不相等" << endl; } int main(int argc, char** argv) { test01(); return 0; }
结果:p1和p2相等
#include <iostream>//写了两个... using namespace std; #include<cstring> class Myprint{//打印类 public: /*======================================================*/ void operator()(string test){ cout << test <<endl; } /*======================================================*/ }; class MyAdd{//加法类 public: /*======================================================*/ int operator()(int a,int b){ return a+b; } /*======================================================*/ }; void test01(){ Myprint myPrint; myPrint("hello word"); MyAdd myAdd; int c = myAdd(1,2); cout << c << endl; //匿名对象 cout << MyAdd()(1,1) << endl; } int main(int argc, char** argv) { test01(); return 0; }
结果:hello word(换行)3(换行)2