C++练级之路——类和对象(下)

2024-04-20 16:44
文章标签 c++ 对象 练级

本文主要是介绍C++练级之路——类和对象(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1、构造函数初始化列表

2、类型转换

3、explicit关键字

4、static成员

5、友元

友元函数

友元类

6、内部类

7、匿名对象

8、拷贝构造时的一些编译器优化

差不多结束了,类和对象!


1、构造函数初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分割的数据成员列表,每个成员变量后面跟着一个放在括号中的初始值或表达式

例如:

typedef int DataType;
class Stack
{
public:Stack(int capacity){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}private:DataType* _array;int _capacity;int _size;
};
class MyQueue
{
public://初始化列表MyQueue(int n = 20):_pushst(n),_popst(n),_size(0){}
//private:Stack _pushst;Stack _popst;int _size;
};
int main()
{MyQueue q1(20);q1._pushst.Push(1);q1._pushst.Push(2);q1._pushst.Push(3);q1._pushst.Push(4);return 0;
}

注意:

1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2.类中包含以下成员,必须放在初始化列表中进行初始化:

引用成员变量

const成员变量

自定义成员变量(且该类没有默认构造函数时)

class A
{
public:A(int a):_a(a){}
private:int _a;
};
class B
{
public:B(int a, int ref):_aobj(a),_ref(ref),_n(10){}
private:A _aobj;  // 没有默认构造函数int& _ref;  // 引用const int _n; // const 
};

3.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后顺序无关

class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}
A. 输出1  1
B.程序崩溃
C.编译不通过
D.输出1  随机值

 选D

4.初始化列表,不管你写不写,每个成员变量都会先走一遍,自定义类型会调用默认构造(没有默认构造就报错)内置类型有缺省值就用缺省值,没有缺省值,不确定,C++没有规定,要看编译器,先走初始化列表,再走函数题

实践中:尽可能使用初始化列表初始化,不方便再用函数体初始化;

缺省参数还可以这样写:
 

class BB
{
public:
BB()
{ }
private:
int _a=1;
int*ptr=(int*)malloc(40);
Stack _s1=10;
A _a1=20;
A _a2={1,2};};

这里的缺省参数只是声明,最后是给初始化列表进行初始化;初始化列表不写,其实编译器也会自动生成。 

2、类型转换

 class A
{
public://explicitA(int a)A(int a):_a(a){cout << "A(int a)" << endl;}A(int a1, int a2):_a(0),_a1(a1),_a2(a2){}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}private:int _a;int _a1;int _a2;
};int main()
{A aa1(1);// 拷贝构造A aa2 = aa1;// 隐式类型转换// 内置类型转换为自定义类型// 3构造一个A的临时对象,在用这个临时对象拷贝构造aa3// 编译器遇到直接构造+拷贝构造->优化为直接构造A aa3 = 3;// raa 引用的是类型转换中用3构造的临时对象 const A& raa = 3;A aaa1(1, 2);A aaa2 = { 1, 2 };const A& aaa3 = { 1, 2 };return 0;
}

当我们直接用 A aa3=3;中间发生了类型转换,3首先构造A的一个临时对象,再用这个临时对象拷贝构造aa3;

当编译器遇到连续构造+拷贝构造,会优化成直接构造 

const A& raa = 3;

raa引用的是3的临时变量,因为临时变量具有常性,所以要加const;

3、explicit关键字

构造函数不仅可以构造初始化对象,对于单个参数或者除第一个参数没有默认值其余均有默认值的构造函数,还具有类型转换的功能。

用explicit修饰构造函数则不能发生类型转换,限制了类型转换

类型转换是一个很好用,很神奇,比较难理解的一点,以后继续慢慢了解!

4、static成员

声明为static的类成员称为类的静态成员,用static修饰的成员变量称为静态成员变量,用static修饰的成员函数称为静态成员函数;

静态成员变量要在类外进行初始化:

 

 static成员变量不在类中创建,而是在静态区,当然static也不能给缺省值,因为缺省值是要进初始化列表的,而static不在初始化列表中进行初始化,所以不能给缺省值,static属于整个类域,在整个类中都可以访问,如果static定义的是public,那么在类外也可以访问;

static修饰成员函数,静态成员函数没有隐藏的this指针,意味着只能访问静态成员变量

kimi.ai的回答:

在C++中,`static`关键字可以用于修饰成员函数,使其成为静态成员函数。静态成员函数具有以下特点:

1. **类函数**:静态成员函数是类的一部分,但它们不依赖于类的任何特定对象实例。

2. **无this指针**:由于静态成员函数不与特定的对象实例相关联,它们在内部实现时没有`this`指针。

3. **类作用域**:静态成员函数只能在类的内部定义,并且它们可以访问类的静态成员,但不能访问非静态成员。

4. **调用方式**:静态成员函数可以通过类名直接调用,而不需要创建类的实例。

5. **不能被虚函数覆盖**:静态成员函数不能被声明为虚函数,因为它们不涉及运行时多态。

6. **存储位置**:静态成员函数的实现代码存储在程序的只读数据段(通常称为文本段)中,与类的其他静态成员一样。

下面是一个简单的C++类,其中包含了一个静态成员函数的例子:

class MyClass {
public:static void staticFunction() {// 静态成员函数的实现// 可以访问静态成员,但不能访问非静态成员}// 非静态成员函数void nonStaticFunction() {// 可以访问静态成员和非静态成员}// 静态成员变量static int staticVar;// 非静态成员变量int nonStaticVar;
};// 定义静态成员变量
int MyClass::staticVar = 0;int main() {// 直接通过类名调用静态成员函数MyClass::staticFunction();// 也可以通过对象调用静态成员函数,但这通常不是推荐的做法MyClass obj;obj.staticFunction();return 0;
}

在这个例子中,`staticFunction`是一个静态成员函数,它可以通过`MyClass::staticFunction()`直接调用,也可以通过对象实例调用,尽管后者不是推荐的做法。静态成员函数非常适合那些不需要访问对象状态的函数,例如工具函数或计算类。

5、友元

友元分为友元函数和友元类

友元函数

友元函数可以访问类中的私有和保护成员,友元函数不是类的成员,所以不会通过this访问类的成员,友元函数的的实现是在类中加一个friend关键字,然后声明;

class MyClass {
private:int privateVar;public:MyClass(int value) : privateVar(value) {}// 声明一个友元函数friend void accessPrivateVar(MyClass& obj);
};// 实现友元函数
void accessPrivateVar(MyClass& obj) {// 访问MyClass的私有成员int value = obj.privateVar;
}int main() {MyClass obj(10);accessPrivateVar(obj);  // 正确,友元函数可以访问私有成员return 0;
}

注意:

1.友元函数可以在类中的任意位置声明;

2.友元函数不能用const修饰;

3.一个函数可以是多个类的友元函数;

友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的成员。

class FriendClass;class MyClass {
private:int privateVar;public:MyClass(int value) : privateVar(value) {}// 声明一个友元类friend class FriendClass;
};class FriendClass {
public:void accessPrivateVar(MyClass& obj) {// 访问MyClass的私有成员int value = obj.privateVar;}
};int main() {MyClass obj(10);FriendClass friendObj;friendObj.accessPrivateVar(obj);  // 正确,友元类可以访问私有成员return 0;
}

 注意:

1.友元关系是单向的,不具有交换性;

2.友元关系不能传递,如果C是B的友元,B是A的友元,不能说明C是A的友元;

3.友元关系不能继承(后续会讲)

6、内部类

如果一个类定义在另一个类的内部,这个类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象访问内部类的成员,外部类对内部类没有任何优越的访问权限。

注意:内部类就是外部类的友元

1.内部类定义在外部类的public protected  private 都是可以的;

2.内部类可以直接访问外部类的static成员,不需要外部类的对象或类名;

3,sizeof(外部类)=外部类,和内部类没有任何关系。

class A
{
private:static int k;int h;
public:class B // B天生就是A的友元{public:void foo(const A& a){cout << k << endl;//OKcout << a.h << endl;//OK}};
};
int A::k = 1;
int main()
{A::B b;b.foo(A());//A()是一个匿名对象,然后const A& a是A()的引用,//相当于延长了A()的生命周期,当foo函数结束时,A()也就析构了return 0;
}

 

7、匿名对象

class A
{
public:A(int a = 0):_a(10){cout << "A(int a = 0)" << endl;}~A(){cout << "~A()" << endl;}
private:int _a;
};int main()
{A();return 0;
}

A()就是个匿名对象,顾名思义,匿名对象没有名字,但他的生命周期只有这一行,我们可以看到他下一行就会调用析构函数;匿名对象的用途我们以后还会说,这里暂时用不到;

 

8、拷贝构造时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还
是非常有用的。
class A
{
public:A(int a = 0):_a(a){cout << "A(int a)" << endl;}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}
A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a = aa._a;}return *this;}~A(){cout << "~A()" << endl;}
private:int _a;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}
int main()
{// 传值传参A aa1;f1(aa1);cout << endl;// 传值返回f2();cout << endl;// 隐式类型,连续构造+拷贝构造->优化为直接构造f1(1);// 一个表达式中,连续构造+拷贝构造->优化为一个构造f1(A(2));cout << endl;// 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造A aa2 = f2();cout << endl;// 一个表达式中,连续拷贝构造+赋值重载->无法优化aa1 = f2();cout << endl;return 0;
}

这些优化只是编译器的优化,不同的编译器优化的效果也不同,这里我们可以了解一下!

差不多结束了,类和对象!

这篇关于C++练级之路——类和对象(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function

【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 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

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对象