目录
面向对象基础
类与对象
概念
类的内容
创建对象
封装
构造函数
基本使用
构造初始化列表
隐式调用与显式调用
拷贝构造函数
浅拷贝
深拷贝
析构函数
作用域限定符:
名字空间
类内声明,类外定义
面向对象基础
类与对象
概念
类:类是一个抽象的概念,用于描述同一类对象的特点。
对象:根据类的概念所创造的实体。必须要先写类才能创建对象。
类的内容
类中最基础的内容包括两个部分,一个是属性,一个是行为。
●属性:表示一些特征项的数值,比如说:身高、体重、肤色、性别、重量、颜色、型号等等。而这些特征项的数值也被称为“成员变量”。属性一般以名词存在。
●行为:表示能执行的动作,能干什么事?比如说:吃饭、睡觉、打架、打篮球。行为一般函数实现,也被称为“成员函数”。行为一般以动词存在。成员 = 成员函数+成员变量。
class MobilePhone// 帕斯卡命名法(大驼峰命名法)
{
public: string brand; // 品牌string model; // 型号int weight; // 重量void play_music(){cout << "音乐ing" << endl;}void run_game(){cout << "LOL、原神、王者荣耀、开心消消乐、" << endl;}void call(){cout << "hello" << endl;}
};
创建对象
1.栈内存对象
#include <iostream>
using namespace std;
int main()
{MobilePhone mp;mp.brand = "xiaomi";mp.model = "11 Pro";mp.weight = 200;cout << mp.brand << " " << mp.model << " " << mp.weight << " " << endl;mp.play_music();mp.run_game();mp.call();return 0;
}
2.堆内存对象
必须使用new关键字创建,使用指针保存。如果不使用delete关键字销毁,则堆内存对象会持续存在。而导致内存泄漏。堆内存对象在调用成员时,使用->而不是"."
#include <iostream>
using namespace std;
int main()
{MobilePhone* mp = new MobilePhone; // 堆内存对象mp->brand = "huawei";mp->model = "mate 30";mp->weight = 200;cout << mp->brand << " " << mp->model << " " << mp->weight << endl;mp->play_music();mp->run_game();mp->call();delete mp; // 手动销毁mp = NULL; return 0;
}
封装
类与结构体差别不大,实际上可以认为结构体就是一种完全开放的类。
封装指的是,将类的一些属性和细节隐藏。重新提供外部访问接口,封装可以提升代码的安全性,并且可以让程序员更关注于上层架构而非内部细节。
#include <iostream>
using namespace std;
class MobilePhone
{
private: // 私有权限,private是最封闭的权限,只能在类内访问string brand; // 品牌string model; // 型号int weight = 200; // 重量public: // 权限:public最开放的权限// geter 读函数string get_brand(){return brand;}// seter 写函数void set_brand(string b){brand = b;}// 读函数string get_model(){return model;}// seter 写函数void set_model(string m){model = m;}// getint get_weight(){return weight;}
};int main()
{MobilePhone mp1; // 栈内存对象mp1.set_brand("pingguo");mp1.set_model("15 Pro");cout << mp1.get_brand() << " " << mp1.get_model() << " " << mp1.get_weight() << endl;MobilePhone *mp2 = new MobilePhone; // 堆内存对象mp2->set_brand("pingguo");mp2->set_model("16 pro");cout << mp2->get_brand() << " " << mp2->get_model() << " " << mp2->get_weight() << endl;return 0;
}
构造函数
基本使用
构造函数是一种特殊的成员函数,用于创建对象时初始化。创建对象时必须直接或者间接调用当前类的任意一个构造函数。
构造函数写法上有以下要求:
1.函数名称必须与类名完全相同
2.构造函数不写返回值
3.如果程序员不手动编写构造函数,编译器会自动添加一个默认的无参数的构造函数,只要添加任意构造函数后,编译器将不再补充默认无参数的构造函数。构造函数在创建对象时,常用于给对象的属性赋予初始值。构造函数也支持函数重载,构造函数也支持函数参数默认值
#include <iostream>
using namespace std;class MyPhone{
private:string pinpai;//品牌string xinghao;//型号int zhongliang;//重量public:MyPhone(){cout <<"调用无参构造函数"<<endl;pinpai="小米";xinghao="11 Pro";zhongliang=205;}MyPhone(string a,string b,int c){cout <<"调用有参构造函数"<<endl;pinpai=a;xinghao=b;zhongliang=c;}string getPinpai(){return pinpai;}void setPinpai(string value){pinpai = value;}string getXinghao(){return xinghao;}void setXinghao(string value){xinghao = value;}int getZhongliang(){return zhongliang;}
};int main()
{MyPhone *mp=new MyPhone("苹果","16 Pro",200);cout <<"手机为"<<mp->getPinpai()<<" "<<mp->getXinghao()<<" 重量为:"<<mp->getZhongliang()<<"g"<<endl;delete mp;mp=NULL;MyPhone a;cout <<"手机为"<<a.getPinpai()<<" "<<a.getXinghao()<<" 重量为:"<<a.getZhongliang()<<"g"<<endl;return 0;
}
构造初始化列表
构造初始化列表是一种更简单的给成员变量赋予初始值的写法。
#include <iostream>
using namespace std;
class MobilePhone
{
private: string brand; string model; int weight;
public: MobilePhone():brand("8848"),model("M6巅峰版"),weight(300){}MobilePhone(string b,string m,int w):brand(b),model(m),weight(w){}string get_brand(){return brand;}string get_model(){return model;}int get_weight(){return weight;}
};int main()
{MobilePhone mp1("小米","su7",300); cout << mp1.get_brand() << " " << mp1.get_model() << " " << mp1.get_weight() << endl;return 0;
}
当构造函数的局部变量与成员变量重名时,除了使用后面学习的this指针的方式外,还可以使用构造初始化列表区分。
隐式调用与显式调用
构造函数的调用可以分为显式调用与隐式调用。
显式调用是指在创建对象时手写构造函数的名称,与参数列表。
隐式调用指的是在创建对象的时候不写构造函数的参数列表,编译器会尝试调用对应参数的构造函数。
#include <iostream>
using namespace std;
class Student
{
private:int age;
public:explicit Student(int a):age(a){cout << "构造函数" << endl;}Student():age(1){cout << "无参构造函数" << endl;}int get_age(){return age;}
};
int main()
{Student s1(12); // 显式调用cout << s1.get_age() << endl;Student s3 = Student(14); // 显式调用cout << s3.get_age() << endl;// Student s4 = 15; // 隐式调用// cout << s4.get_age() << endl;Student* s2 = new Student(13); // 显式调用cout << s2->get_age() << endl;Student s5;cout << s5.get_age() << endl;return 0;
}
建议使用显式调用,可以使用explict关键字屏蔽隐式调用语法。
拷贝构造函数
当程序员不手写拷贝构造函数时,编译器会自动添加一个拷贝构造函数,使对象创建可以通过这个构造函数实现。
#include <iostream>
using namespace std;
class Student
{
private:int age;
public:Student(int a):age(a){cout << "构造函数" << endl;} Student(const Student &st){age = st.age;cout << "拷贝构造函数被调用了" << endl;}int get_age(){return age;}
};
int main()
{Student s1(12);cout << s1.get_age() << endl;Student s2(s1); // 拷贝构造函数cout << s2.get_age() << endl;return 0;
}
拷贝构造函数存在隐患,当成员变量出现指针类型时,默认的拷贝构造函数会导致两个对象的成员变量指向同一处,不合符面向对象的设计规范,这种现象被称为“浅拷贝”。
浅拷贝
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:char *name;
public:Dog(char *n){name = n;}void show_name(){cout << name << endl;}
};
int main()
{char arr[20] = "旺财";Dog d1(arr);Dog d2(d1); // 拷贝构造函数strcpy(arr,"大黄"); // 更改外部内存,对象内部的数据也跟着进行更改,因为操作的同一块内存空间d1.show_name(); // 大黄d2.show_name(); // 大黄return 0;
}
这种情况必须手动重写构造函数,使每次赋值都创建一个新的副本,从而每个对象单独持有自己的成员变量,这种方式被称为“深拷贝”。
深拷贝
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:char *name;
public:Dog(char *n){name = new char[20];strcpy(name,n);}Dog(Dog &d){name = new char[20];strcpy(name,d.name);}void show_name(){cout << name << endl;}
};
int main()
{char arr[20] = "旺财";Dog d1(arr);Dog d2(d1); // 拷贝构造函数strcpy(arr,"大黄");d1.show_name(); // 旺财d2.show_name(); // 旺财return 0;
}
在需求不受影响的情况下,可以通过屏蔽拷贝构造函数(私有化),来解决浅拷贝的问题。深拷贝的代码也存在隐患,new开辟的空间无法方便的进行释放,造成内存泄漏的问题。
补充:
1、添加无参构造函数,编译器还会添加默认无参构造函数嘛?(不会)
2、添加任意构造函数,编译器还会添加默认无参构造函数嘛?(不会)
3、添加无参构造函数,编译器还会添加默认拷贝构造函数嘛?(会)
4、添加拷贝构造函数,编译器还会添加默认拷贝构造函数嘛?(不会)
析构函数
析构函数是与构造函数对立的函数。
构造函数 | 析构函数 |
创建对象时手动调用 | 当对象销毁时,自动调用 |
函数名称是类名 | 函数名称是~类名 |
构造函数可以重载 | 析构函数没有参数,不能重载 |
用于创建对象时并初始化 | 用于销毁对象时释放资源 |
有返回值但是不写,返回值是新创建的对象 | 没有返回值 |
#include <iostream>
#include <string.h>
using namespace std;
class Dog
{
private:char *name;
public:Dog(char *n){name = new char[20];strcpy(name,n);}Dog(Dog &d){name = new char[20];strcpy(name,d.name);}void show_name(){cout << name << endl;}~Dog(){cout << "析构函数被调用了" << endl;delete []name;}
};
int main()
{char arr[20] = "旺财";Dog d1(arr);Dog d2(d1); // 拷贝构造函数strcpy(arr,"大黄");d1.show_name(); // 旺财d2.show_name(); // 旺财Dog *d3 = new Dog(arr);delete d3; // 销毁时调用析构函数return 0;
}
作用域限定符:
名字空间
#include <iostream>
#include <string.h>
using namespace std;
namespace my_space// 名字空间
{int a = 3;int b = 4;
}
using namespace my_space;
int a = 2;
int main()
{
int a = 1;
cout << a << endl; // 就近原则,打印1
cout << ::a << endl; // ::全局作用域(匿名名字空间)2
cout << my_space::a << endl; // 3
cout << b << endl; // 4
return 0;
}
类内声明,类外定义
#include <iostream>
using namespace std;
class Demo
{
public:// 类内声明Demo();void test(string str);
};
// 类外定义
Demo::Demo()
{cout << "创建了一个对象" << endl;
}
void Demo::test(string str)
{cout << "string:" << str << endl;
}
int main()
{Demo d1;d1.test("hello");return 0;
}