C++(名字空间/面向对象/类和对象/构造函数和析构函数/const成员和初始化列表)

本文主要是介绍C++(名字空间/面向对象/类和对象/构造函数和析构函数/const成员和初始化列表),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、名字空间

什么是名字空间

在C语言中定义的全局变量、函数、结构、联合、枚举、枚举值、宏都在全局作用域下,所以当项目比较庞大时,非常容易造成命名冲突(以模块名作前缀、后缀),所以C++中选择把全局作用域进行拆分成 子作用域进行管理,这些子作用域就是作名字空间。

如何设计名字空间
namespace 空间名 {// 子作用域在该作用域中定义全局变量、函数、结构、联合、枚举、枚举值...,不会与全局变量中的命名冲突
} // 此处没有分号
如何使用名字空间中的内容
方法1:

直接使用空间中的内容

空间名::标识符;

#include <iostream>
​
int main(int argc,const char* argv[])
{std::cout << "我要使用std中的内容" << std::endl;        return 0;
}
方法2:

把空间中的部分内容导入到当作用域下,一旦导入之后就可以方便的直接使用

using 空间名::标识符;

#include <iostream>
​
using std::cout;
using std::endl;
​
int main(int argc,const char* argv[])
{cout << "我要使用std中的内容" << endl;              return 0;
}
方法3:

把空间中的所有内容导入到当前作用域下

using namespace 空间名;

#include <iostream>
​
using namespace std;
​
int main(int argc,const char* argv[])
{string str = "string类也定义在std名字空间中";cout << str << endl;return 0;
}

注意:C++标准库中提供的基础功能的类、对象都定义在std名字空间中,如:cout、cin、string。

空间合并与空间嵌套

1、编译器会把同名的名字空间的内容自动合并,同名的名字空间就是同一个作用域,如果同一个作用域 下有命名冲突编译就会报错。

#include <iostream>
using namespace std;
​
namespace ns {int num1 = 1234;
}
​
namespace ns {int num2 = 5678;int num1 = 6666;
}
​
int main(int argc,const char* argv[])
{using namespace ns;cout << num1 << " " << num2 << endl;return 0;
}

2、名字空间中可以再定义名字空间

#include <iostream>
using namespace std;
​
namespace ns1 
{namespace ns2 {   namespace ns3 {int num = 123456789;}}   
}
​
// 嵌套后的名字空间使用起来比较长,可以给内部名字空间取别名
namespace ns123=ns1::ns2::ns3;
​
int main(int argc,const char* argv[])
{cout << ns1::ns2::ns3::num << endl;cout << ns123::num << endl;return 0;
}
匿名名字空间

C++把默认的全局作用域当作一个没有名字的名字空间,也叫匿名名字空间,当全局的标识符被屏蔽,可以使用匿名空间的域限定符来指定全局作用域的标识符。

#include <iostream>
using namespace std;
​
int num = 1234;
int main(int argc,const char* argv[])
{int num = 5678;cout << num << endl;cout << ::num << endl;return 0;
}
注意:

工作时尽量不要使用 using namespace 空间名; 这种用法,这就像是把垃圾分类之后,又合并了,如果使用到标识符,一定要 空间名::标识符。

二、面向过程与面向对象

面向过程:

在编程时重点考虑如何解决问题,以及解决问题的具体步骤。

面向对象:

在编程时重点考虑的是"谁"能解决问题(类、结构),以及"它"解决问题时所需要属性(成员变量)和功能(成员函数)。

抽象:

把“解决问题者”当作思考或观察对象,把解决问题所需的具备的属性和功能罗列出来,这个步骤叫作抽象。

封装:

把抽象的结果(问题所需的具体的属性和功能),设置相应访问权限(public/private/protected),按照C++的语法设计成类、结构,该过程叫作封装类,简称封装。

使用封装好的类、结构,实例化出对象(定义结构变量、类变量),通过对象调用相关功能(成员函数)配合相关属性(成员变量)达到解决问题的目的。

继承:

1、在解决一个问题之前,先考虑现有的类是否能解决部分问题,如果有则继承该类,在此基础上进行扩展,以缩短解决问题的时间(代码复用)。

2、在解决一个复杂庞大的问题时,把问题拆分成若干个小问题,每个小问题实现一个类去解决,最后把这若干个类通过继承进行汇总,达到解决问题的目的,这个方式可以降低问题的规模在难度,也方便团队分工、协作。

多态:

语句的多种形态,同样的语句根据环境或参数的不同,有不同的功能,这就叫多态。

多态可以分为:

编译时多态,编译器在编译代码时根据语句的参数、环境不同,能翻译出具有不同功能的二进制指令,例如:调用重载的函数,cin、cout的自动类型识别,模板技术(后续讲解)。

运行时多态,语句只在程序运行时,才能确定执行那种功能,一般指的是类多态,后续再详细讲解。

#include <iostream>
using namespace std;
​
void func1(void)
{cout << "我是func1函数" << endl;
}
​
void func2(void)
{cout << "我是func2函数" << endl;
}
​
int main()
{void (*fp)(void);int cmd;cin >> cmd;if(cmd % 2)fp = func1;elsefp = func2;fp();
}

三、类和对象

什么是类

把抽象结果(利用面向对象的思维模式,思考、观察出的结果),使用用C++的语法封装出一种类似结构的自定义数据类型(复合数据类型)。

如何设计类
struct 结构名
{成员函数;   // 结构的成员默认访问权限是public成员变量;
};
​
class 类名
{成员变量;   // 结构的成员默认访问权限是private成员函数;
};
​
注意:在C++中类的结构除了成员的默认访问权限不同,没有任何区别(大师兄说的)。
访问限制符

使用以下关键字修改结构、类的成员,管理它们的访问权限:

private: 私有的,被它修饰的成员,只能在类的成员函数内使用。
protected: 保护的,被它修饰的成员,只能在类、子类的成员函数内使用。
public: 公开的,被修饰的成员,可以在任何位置使用。
#include <iostream>
using namespace std;
​
struct Student
{void in(void){   cout << "请输入学生的学号、姓名、年龄、成绩:"; cin >> id >> name >> age >> score;}   void out(void){   // 学号:10010 姓名:hehe 年龄:18 成绩:100cout << "学号:" << id << " 姓名:" << name << " 年龄:" << age << " 成绩:" << score << endl;}   
private:int id; char name[20];short age;float score; 
};
​
class Student
{int id; char name[20];short age;float score;   
public:void in(void){   cout << "请输入学生的学号、姓名、年龄、成绩:"; cin >> id >> name >> age >> score;}   void out(void){   // 学号:10010 姓名:hehe 年龄:18 成绩:100cout << "学号:" << id << " 姓名:" << name << " 年龄:" << age << " 成绩:" << score << endl;} 
};
​
int main(int argc,const char* argv[])
{Student stu;stu.in();stu.out();return 0;
}
什么是类对象

使用设计好的类(结构)这种数据类型,定义出的类变量在面向对象编程语言中被称为对象(结构变量),创建类对象的行为也被称为实例化对象。

#include <iostream>
using namespace std;
​
class Student
{int id; char name[20];short age;float score;
public:void in(void){   cout << "请输入学生的学号、姓名、年龄、成绩:"; cin >> id >> name >> age >> score;}   void out(void){   // 学号:10010 姓名:hehe 年龄:18 成绩:100cout << "学号:" << id << " 姓名:" << name << " 年龄:" << age << " 成绩:" << score << endl;}   
};
​
Student stu; // 在bss内存段上实例化对象
int main(int argc,const char* argv[])
{Student stu; // 在stack内存段实例化对象Student* stup = new Student; // 在heap内存段实例化对象
}

类的声明与实现分开

如果类的内容不多,一般会选择只定义一个头文件,以类名作为文件名。

#ifndef STUDENT_H
#define STUDENT_H
​
class 类名
{// 定义成员变量
public:// 定义成员函数
};
​
#endif//STUDENT_H

如果类的内容比较多,会选择把类的声明与定义分开,头文件负责类的声明,源文件负责类的实现。

#ifndef STUDENT_H
#define STUDENT_H
​
class 类名
{// 定义成员变量
public:// 声明成员函数void 函数名(参数列表);
};
​
#endif//STUDENT_H
#include "student.h"
​
void 类名::函数名(参数列表)
{}
练习:实现C++版的通讯录。

四、构造函数与析构函数

构造函数
1、什么构造函数

类、结构、联合中的特殊成员函数,与类名、结构名、联合名同的成员函数,没有返回值。

class 类名
{
public:类名(参数列表){}
};
2、何时调用构造函数

当创建类对象时(实例化对象)会自动调用构造函数。

int main(int argc,const char* argv[])
{// 调用无参构造Student stu;Student* stup = new Student;
​// 调用有参构造Student stu1(10010,"hehe",'w',18,98);Student* stup1 = new Student(10011,"xixi",'m',20,95);// 调用有参构造,c11语法标准才支持,-std=c++11Student stu2 = {10010,"hehe",'w',18,98};return 0;
}
3、在构造函数中应该做什么

构造函数负责创建使用对象的环境,一般构造函数中负责:

1、给成员变量初始化

2、给指针成员分配内存

3、从文件、网络、数据库加载数据

4、完成一些准备工作

4、实现构造函数要注意的问题

在创建类对象时,一定会调用构造函数,如果类中没有显式实现构造函数,编译器会自动生成一个无参的什么都不做的构造函数。

如果显式实现了有参构造函数,那么编译器就不会再生成无参构造。

注意:在使用new[] 创建n个对象时,无法保证给所有的类对象都单独提供参数,去调用有参构造,那么必然需要调用无参构造,此时如果没有显式实现无参构造,编译就会出错。

方法1:

class Student
{
public:Student() {} // 极简无参构造
};

方法2:

给有参构造函数的所有成员都设置默认形参,当需要调用无参构造时,会自动使用默认参数调用有参构造。

// 无参构造未必无参
class Student
{...
public:Student(int _id=0,const string& _name="",char _sex='w',short _age=0,float _score=0){id = _id;name = _name;sex = _sex;age = _age;score = _score;cout << "我是有参构造" << endl;}
};
5、explicit关键字的作用

如果类中有单参的构造函数,那么它的参数类型数据就能隐藏提升为类对象,如果不使用该功能,可以在单参构造函数的前面加 explicit 关键字,参数类型数据就不能再隐藏提升为类对象。

总结:explicit关键字的作用就是禁止单参构造函数的类型提升功能。

#include <iostream>
using namespace std;
​
class Test
{int num;
public:[explicit] Test(int _num){   num = _num;}    
};
​
void func(Test t)
{cout << "我被调用了" << endl;
}
​
int main(int argc,const char* argv[])
{func(1234);
}

析构函数:
1、什么析构函数

类、结构、联合中的特殊成员函数,在类名、结构名、联合的前面加~,就是析构函数,没有返回也没有参数。

struct Student
{int id; string name;char sex;short age;float score;
public:Student(void){cout << "我是无参构造函数" << endl;}Student(int _id,const string& _name,char _sex,short _age,float _score){   id = _id;name = _name;sex = _sex;age = _age;score = _score;cout << "我是有参构造" << endl;}   ~Student(void){   cout << "我是析构函数" << endl;}   
};
2、在析构函数中应该做什么

析构函数负责对象销毁时的收尾工作,一般构造函数中负责:

1、释放指针成员指向的堆内存

2、往文件中、数据库保存数据

3、何时调用析构函数

1、当类对象离开它所在的作用域时,系统会自动销毁类对象,并自动调用析构函数。

#include <iostream>
using namespace std;
​
struct Student
{int id;string name;char sex;short age;float score;
public:Student(void){cout << "我是无参构造函数" << endl;}Student(int _id,const string& _name,char _sex,short _age,float _score){id = _id;name = _name;sex = _sex;age = _age;score = _score;cout << "我是有参构造" << endl;}~Student(void){cout << "我是析构函数" << endl;}
};
​
int main(int argc,const char* argv[])
{do{// 当创建类对象时,自动调用构造函数,当对象离开它的作用域时,会自动调用析构函数Student stu;}while(0);cout << "-----------" << endl;
}

2、当使用delete或delete[] 释放堆内存上的类对象时,会自动调用析构函数(必须使用delete释放)。

#include <iostream>
using namespace std;
​
struct Student
{int id;string name;char sex;short age;float score;
public:Student(void){cout << "我是无参构造函数" << endl;}Student(int _id,const string& _name,char _sex,short _age,float _score){id = _id;name = _name;sex = _sex;age = _age;score = _score;cout << "我是有参构造" << endl;}~Student(void){cout << "我是析构函数" << endl;}
};
​
int main(int argc,const char* argv[])
{
​// 使用new 或 new[] 在堆内存上创建类对象时会自动调用构造函数Student* stup = new Student;Student* stus = new Student[10];
​// 类对象的个数,也是执行构造、析构函数的次数cout << *((int*)stus-1) << endl;
​// 使用delete 或 delete[] 释放堆内存上的类对象时会自动调用析构函数delete stup;delete[] stus;
​// 注意:使用new创建的就使用delete释放,使用new[]创建的就使用delete[]释放,不能混用return 0;
}
4、析构函数要注意的问题

如果没有显式实现析构函数,那么编译器会自动生成一个什么都不做的析构函数。

如果构造函数中使用new、malloc分配的堆内存,就一定要显式实现析构函数,并在析构函数中释放堆内存,否则就会造成内存泄漏。

五、初始化列表与const成员

const成员

使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。

不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。

在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。

初始化列表

在构造函数小括号后面,主要用于给const成员初始化的一种特殊语法,也可以给普通成员初始化(可以解决参数名与成员变量名同名的问题)。

注意:如果有成员是类、结构、联合类型的,还可以在初始化列表中显式调用成员的构造函数,还可以调用父类的构造函数。

class 类名
{成员变量;
public:构造函数(参数列表):成员变量(初始值),成员变量(初始值),...{}
};
#include <iostream>
using namespace std;
​
class Test
{// 可以使用,但只有C11标准才支持// const int num = 1234;const int num;
public:Test(int num=0):num(num){   cout << "我是带初始化列表的构造函数" << endl;}   void show(void){   cout << "const num = " << num << endl;}   
};
int main(int argc,const char* argv[])
{Test t(123456789);t.show();return 0;
}

这篇关于C++(名字空间/面向对象/类和对象/构造函数和析构函数/const成员和初始化列表)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1114625

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现