本文主要是介绍2-C++的类与对象、封装、构造函数、拷贝、构析函数、作用域限定符的运用和explicit关键字的相关知识点,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
1.类和对象
2.封装
3.构造函数
3.1 固定值的构造函数
3.2 有参的构造函数
3.3 构造函数支持重载
4.深拷贝与浅拷贝()
5.析构函数
6.作用域限定符::
6.1 名字空间
6.2类内声明 内外定义
7.explicit关键字
1.类和对象
Car类 属性:brand 品牌 seat 座位数量 wheel 轮子数量
方法 run() xx牌的汽车在行驶
show() 打印出上面的属性信息
分别实例化栈和堆内存对象。来调用方法
#include <iostream>
using namespace std;
class Car{
public://brand 品牌 seat 座位数量 wheel 轮子数量
string brand;int seat;int wheel;void run(){
cout<<brand<<"牌的汽车在行驶"<<endl;}void show(){
cout<<"品牌:"<<brand<<" 座位数量:"<<seat<<" 车轮数量:"<<wheel<<endl;}};
int main()
{//栈内存对象
Car car1; //创建car1对象
car1.brand="奥迪";
car1.seat=5;
car1.wheel=4;
car1.run();
car1.show();//堆内存对象
Car * car2=new Car;
car2->brand="宝马";
car2->seat=4;
car2->wheel=4;
car2->run();
car2->show();delete car2;
car2=NULL;}
写一个长方形的类 Rectangle .
属性 长length 宽 width
方法 面积 area 周长 perimeter
#include <iostream>
using namespace std;
class Rectangle{
public:int width;int length;void area(){
cout<<"面积是:"<<width*length<<endl;}int perimeter(){return (width+length)*2;}
};
int main()
{
Rectangle r1;
r1.width=10;
r1.length=20;
r1.area();
cout<<r1.perimeter()<<endl;
}
2.封装
概念:将类中的一些属性和具体实现细节隐藏。通常是把属性设为私有。如需访问需要通过公共接口。封装可以控制属性的读和写的权限,提高程序的安全性
#include <iostream>
using namespace std;
class Person{
private: //私有权限 类外不能访问string name; //姓名 可读可写string address; //地址 只读string password; //密码 只写
public:void get_name(){
cout<<"姓名是:"<<name<<endl;}void set_name(string n){
name=n;}void get_address(){
cout<<"济南银荷大厦"<<endl;}void setPassword(string p){
password=p;
cout<<"密码重置成功!"<<endl;}};
int main()
{Person p;
p.set_name("小明");
p.get_name();
p.get_address();
p.setPassword("123789#");//cout<<p.password<<endl; //封装属性为私有 类外就访问不到}
3.构造函数
概念:用于创建对象时初始化对象中的属性。
特点:
- 实例化对象时,必须调用构造函数进行初始化。
- 如果不显示的给出构造函数,会有一个默认的构造函数,参数为空,函数体为空,并没有实际意义
- 如果给出任意构造函数,默认无参的构造函数就不存在了
构造函数是个特殊的函数:
- 名字和类同名
- 不需要写返回值
3.1 固定值的构造函数
可以给出有固定值的构造函数。这样的构造函数不需要传参
#include <iostream>
using namespace std;
class Computer{
private:string brand;string model;int weight;public://默认的构造函数
// Computer(){// }Computer(){
cout<<"有固定值的构造函数调用"<<endl;
brand="联想";
model="air14";
weight=200;}void show(){
cout<<"brand:"<<brand<<" model:"<<model<<" weight:"<<weight<<endl;}};
int main()
{Computer c1;
c1.show();
}
3.2 有参的构造函数
用参数给属性赋予初始值,让对象的创建更加灵活
#include <iostream>
using namespace std;
class Computer{
private:
string brand;
string model;
int weight;public:
Computer(string b,string m,int w){
brand=b;
model=m;
weight=w; }
Computer(){
cout<<"有固定值的构造函数调用"<<endl;
brand="联想";
model="air14";
weight=200;
}
void show(){
cout<<"brand:"<<brand<<" model:"
<<model<<" weight:"<<weight<<endl;
}};
int main()
{
Computer c1;
c1.show(); Computer c2("联想","air15",240); //有参构造创建的对象c2
c2.show(); Computer * c3=new Computer("联想","拯救者",270);
c3->show();}
3.3 构造函数支持重载
3.4 构造初始化列表
#include <iostream>
using namespace std;
class Computer{
private:
string brand;
string model;
int weight;public:
//第一种方式 普通构造函数
// Computer(string b,string m,int w){
// brand=b;
// model=m;
// weight=w;// }
//第二种方式 构造初始化列表
Computer(string b,string m,int w):brand(b),model(m),weight(w){}
void show(){
cout<<"brand:"<<brand<<" model:"
<<model<<" weight:"<<weight<<endl;
}};
int main()
{
Computer c2("联想","air15",240); //有参构造创建的对象c2
c2.show(); Computer * c3=new Computer("联想","拯救者",270);
c3->show();}
3.4 构造函数支持默认值
幼儿园老师Kids_Teacher
属性有姓名,性别(性别默认是女),年龄。并给出属性的读取和写入接口。
给出show方法可以打印出上述属性,用构造函数的方式实例化对象
#include <iostream>
using namespace std;
class Kids_Teacher {
private: //姓名,性别(性别默认是女),年龄
string name;
string sex;
int age;
public:
Kids_Teacher(string n,int a,string s="女"){
name=n;
sex=s;
age=a;
}
//属性的读写接口
string getName(){
return name;
}
void setName(string n){
name=n;
}
void show(){
cout<<name<<" "<<sex<<" "<<age<<endl;
}
};
int main()
{
Kids_Teacher t1("小红",20);
t1.show(); //小红 女 20
t1.setName("小花"); //通过set接口重新设置姓名
t1.show(); //小花 女 20
}
- 拷贝构造函数
概念:通过已存在的对象为新对象的数据成员完成初始化。
拷贝构造函数的参数是 对象的引用或者 const修饰的对象引用。它可以将已存在对象的属性值复制给新的对象中
特点:
拷贝构造函数是个特殊的函数,与构造函数构成重载
如果不显示给出拷贝构造函数,编译器会给出默认的构造函数,完成两个对象之间的值复制
对象之间是相互独立的的实体,数据也是相互独立
#include <iostream>
using namespace std;
class MobliePhone{
private:
string brand;
string model;
int weight;
public:
MobliePhone(string b,string m,int w):brand(b),model(m),weight(w){}
void show(){
cout<<"品牌:"<<brand<<" 型号:"<<model<<" 重量:"<<weight<<endl;
}//不写出拷贝构造会给出与下方类似的默认拷贝构造
MobliePhone(MobliePhone& other){
brand=other.brand;
model=other.model;
weight=other.weight;
}
void getBrand(){
cout<<&brand<<endl;
}
};
int main()
{
MobliePhone phone1("华为","P40",100);
phone1.show(); MobliePhone phone2(phone1);
phone2.show(); cout<<&phone1<<" "<< &phone2<<endl; //0x61fe74 0x61fe68
phone1.getBrand(); //0x61fe74
phone2.getBrand(); //0x61fe68}
4.深拷贝与浅拷贝
当对象中的属性有指针类型的时候,需要显示的写出构造函数
浅拷贝
定义:如果不显示的给出拷贝构造函数。会有默认的拷贝构造函数,完成对象之间简单的值赋值,简单值赋值的操作称为浅拷贝
如果对象中的属性有指针类型的时候,默认拷贝构造也只会简单的地址值的复制。导致两个对象的属性指向同一块内存。破坏了对象之间的独立性
#include <iostream>
#include <cstring>
using namespace std;
class MobliePhone{
private:
char * brand;
public:
MobliePhone(char * ch){
brand=ch;
}
void show(){
//cout后面如果是char *类型 ,它会自动打印出字符串//可以通过void * 强转,输出brand中存的字符串地址
cout<<brand<<endl;
cout<<(void *)brand<<endl;
}
};
int main()
{
char a[10]="xiaomi";
cout<<"字符串a的首地址:"<<(void *)a<<endl; //0x61fe86
MobliePhone phone1(a);
phone1.show(); //0x61fe86 MobliePhone phone2(phone1);
phone2.show(); //0x61fe86 strcpy(a,"redmi"); phone1.show(); //0x61fe86
phone2.show(); //0x61fe86 }
深拷贝
通过拷贝构造函数初始化新对象时。应该为新对象的指针类型的属性分配新的内存空间
#include <iostream>
#include <cstring>
using namespace std;
class MobliePhone{
private:
char * brand;
public:
MobliePhone(char * ch){
//brand=ch;
brand=new char[10]; //brand指向了新的一块内存
strcpy(brand,ch); //把字符串的内容复制到brand指向的内存中
}
void show(){
//cout后面如果是char *类型 ,它会自动打印出字符串
//可以通过void * 强转,输出brand中存的字符串地址
cout<<brand<<endl;
cout<<(void *)brand<<endl;
}
MobliePhone(const MobliePhone& other){
brand=new char[10]; //拷贝构造函数创建的对象的brand指向了新的一块内存
strcpy(brand,other.brand); //只拷贝内容
}};
int main()
{
char a[10]="xiaomi";
cout<<"字符串a的首地址:"<<(void *)a<<endl;
MobliePhone phone1(a);
phone1.show(); MobliePhone phone2(phone1);
phone2.show(); strcpy(a,"redmi"); phone1.show();
phone2.show();}
5.析构函数
对象生命期结束前应该完成对象资源的清理,这个工作由析构函数完成。比如创建对象时为数据成员开辟的空间,会通过析构函数在对象的生命期结束前进行释放。
若类中没有显式定义析构函数,则编译器会给出一个默认的析构函数,函数体为空,在对象生命期结束时默认的析构函数被执行。
默认析构函数形式:~类名(){ }
特点:
1.析构函数没有参数,所以不能重载。
2.对象销毁时自动调用
#include <iostream>
#include <cstring>
using namespace std;
class Cat{
private:
string name;
public:
Cat(string n):name(n){}
~Cat(){
cout<<name<<"挂掉了"<<endl;
}
};
void test(){
Cat c1("tom");
Cat * c2=new Cat("加菲"); //这时c2指向的内存不会自动销毁
}int main()
{
test();
}
new关键字创建的堆内存对象需要手动delete销毁,才会自动调用析构函数
#include <iostream>
#include <cstring>
using namespace std;
class Cat{
private:
string name;
public:
Cat(string n):name(n){}
~Cat(){
cout<<name<<"挂掉了"<<endl;
}
};
void test(){
Cat c1("tom");
Cat * c2=new Cat("加菲");
delete c2;
c2=NULL;}
int main()
{
test();
}
创建对象时如果为数据成员开辟了空间,需要通过析构函数在对象的生命期结束前进行释放
#include <iostream>
#include <cstring>
using namespace std;
class MobliePhone{
private:
char * brand;
public:
MobliePhone(char * ch){
//brand=ch;
brand=new char[10];
strcpy(brand,ch);
}
void show(){ cout<<brand<<endl;
cout<<(void *)brand<<endl;
}
MobliePhone(const MobliePhone& other){
brand=new char[10];
strcpy(brand,other.brand);
}
~MobliePhone(){
cout<<"析构函数调用"<<endl;
delete [] brand;
}
};
int main()
{
{
char a[10]="xiaomi";
cout<<"字符串a的首地址:"<<(void *)a<<endl;
MobliePhone phone1(a);
phone1.show(); MobliePhone phone2(phone1);
phone2.show();
}
cout<<"————————————————————"<<endl;}
6.作用域限定符::
6.1 名字空间
命名空间实际上是由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间区域,把一些自己定义的变量、函数等标识符存放在这个空间中,从而与其他实体定义分隔开来。
std是C++标准库的一个名字空间,很多使用的内容都是来自于标准名字空间,例如字符串std::string、std::cout...
当项目中包含using namespace std;时,代码中使用std名字空间中的内容就可以省略前面的std::
类似于不同班级可能有同名学生,可以加上班级名限定进行区分同名学生。
可以让不同的名字空间,存在相同的变量名和函数名。
#include <iostream>
using namespace std;
int a=20;
namespace mySpace {
int a=30;
}
using namespace mySpace;
int main()
{
int a=10;
cout<<a<<endl; //10
cout<<::a<<endl; //20
cout<<mySpace::a<<endl; //30}
6.2类内声明 内外定义
当函数的声明和定义分离时,需要用到作用域限定符::指明函数是属于哪个范围的
#include <iostream>
using namespace std;
class Student{
private:
string name;
int age;
public:
Student(string n,int a);
string getName();
int getAge();
void setName(string n);
void setAge(int a);
};
//实现
Student::Student(string n,int a){
name=n;
age=a;
}
string Student::getName(){
return name;
}
int Student::getAge(){
return age;
}
void Student::setName(string n){
name=n;
}
void Student::setAge(int a){
age=a;
}
int main()
{
Student s("小明",20);
cout<<s.getAge()<<" "<<s.getName()<<endl;}
7.explicit关键字
等号赋值时,等号的左边是对象类型,等号右侧恰好是构造函数可以接受的类型,这时编译器就会自动把等号右边的数据传入到构造函数中。相当于隐式调用了构造函数
#include <iostream>
using namespace std;
class Cow{
private:string name;
public:Cow(string n){
name=n;}void show(){
cout<<name<<endl;}};int main()
{Cow c1("小花");
c1.show();string str="小白";Cow c2=str; //隐式调用构造函数
c2.show();}
在对象操作过程中,可能不小心隐式调用了构造函数,造成错误。为了屏蔽隐式构造,可以加explicit关键字
#include <iostream>
using namespace std;
class Cow{
private:string name;
public:explicit Cow(string n){
name=n;}void show(){
cout<<name<<endl;}};
int main()
{Cow c1("小花");
c1.show();string str="小白";//Cow c2=str; //不允许隐式构造
}
这篇关于2-C++的类与对象、封装、构造函数、拷贝、构析函数、作用域限定符的运用和explicit关键字的相关知识点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!