侯杰C++学习之路:高级面向对象编程-STL库-C++11新功能-cmake
1.1. C 与 C++的区别
C语言有两个主要的内容类别:数据和处理数据的函数,它们相互分离,具有多对多的关系。由于不同的函数可以调用相同的数据,因此在开发大型项目时,两个函数交织在一起很容易导致问题。
因此,面向对象的C++语言使用类来封装函数和数据,使得数据只能由特定的函数来处理。它的作用类似于结构体,有效地建立了数据之间的多对多关系。切换到一对一关系可以避免许多程序错误。
虽然C 结构体当然可以在某种程度上用于创建复杂的数据类型并模拟面向对象编程(OOP) 概念,但与C++ 类相比,C 结构体的类数量仍然有限,存在重要的限制和差异。这就是为什么C++引入了面向对象编程的概念。以下是一些要点:
一般来说,一些面向对象的概念可以用结构体指针、函数指针等技术在C语言中模拟出来,但C++通过类提供了更完整、直接的支持,面向对象使编程更加自然和高效。因此,C++及其面向对象特性的引入极大地简化了代码的组织和管理,使其更具可维护性和可扩展性。
1.2. 头文件的防御式声明
在头文件中编写代码时,应创建防御性语句,以避免每次执行调用头文件的主文件时都重新加载头文件的内容,从而降低程序执行效率。在头文件中,COMPLEX是一个唯一定义的名称,用于区分不同的头文件,如下所示。
#ifndef __复杂__
#定义__COMPLEX__
.
万一
这样,主程序只需在第一次调用时就读取头文件的完整内容,无需重复读取重复内容。
1.3. 头文件的布局
#ifndef __复杂__
#定义__COMPLEX__
//前向声明
包括#cmath
ostream 类;
类综合体。
复杂的
__doapl (complex8* this, const 复杂r);
//类声明
阶级复合体
{
.
};
//类定义
复杂的: 功能.
万一
1.4. inline(内联)函数
简单地说,定义为内联函数的函数运行效率更高,但即使您将所有函数定义为内联函数,也不能保证您的程序会更高效。这是因为这取决于该函数最终是否是内联函数。对于编译器来说,inline 关键字只是一个建议。如果函数是在类的主体中定义的,则它自动成为内联的“候选者”。
类复合体
{
公共:
复数(双r=0,双i=0)
: Ri(r), Im(i)
{}
复合运算符+=(const complex);
double real () const { 返回值}
double imag () const {返回值}
私人:
杜布利,我。
朋友复杂__doapl(complex*, const complex);
};
内联双
ima(const 复数x)
{
返回x.image()。
}
1.5. access level(访问级别)
对于私有域数据,根据是否要向他人展示而将方法(函数)分开,并将其放置在公共部分和私有部分中。
1.6. 构造函数和析构函数
构造函数不能在类中使用。构造函数仅用于实例化类。大多数没有指针的类不需要手动释放内存,因此不需要创建析构函数。可以有很多构造函数(在现实场景中经常使用重载)。
初始化参数列表:complex(double r=0, double i=0) : re(r), im(i) {},如果初始化参数列表中没有给变量赋值初始值,但变量在内部赋值构造函数体相当于跳过了参数初始化过程,大多数情况下不会影响继承,但会降低程序的效率。
类复合体
{
公共:
复数(双精度r=0,双精度i=0)
: Ri(r), Im(i)
{}
复杂(): re(r),im(i){}
复合运算符+=(const complex);
double real () const { 返回值}
double imag () const {返回值}
私人:
杜布利,我。
朋友复杂__doapl(complex*, const complex);
};
void real(double r) { re=r;
在上面的例子中,我们重载了实际的函数。虽然函数名相同,但实际编译后的函数名不同:
?真实@复杂@@QBENXZ
?真实@复杂@@AQENABN@Z
//这个构造函数有两个参数r 和i,它们都提供默认值。这意味着即使没有指定参数或部分参数,也可以调用构造函数。例如:
复杂(双r=0,双i=0): re(r),im(i){}
//这个构造函数没有参数。这是一个典型的默认构造函数,用于初始化对象而不指定任何参数。
复杂(): re(r),im(i){}
上面的两个构造方法是冲突的。在C++中,构造函数重载是根据参数的数量和类型来确定的。在代码中,带有默认参数的构造函数complex(double r=0, double i=0) 基本上已经涵盖了无参数的情况。
因此,这两个构造函数之间存在歧义,因为编译器无法清楚地区分它们。
诸如complete c1;和complex c2();之类的实例化方法不传递任何参数,并且可以解释为complex(double r=0, double i=0)或Complex()。这会阻止编译器在调用构造函数时知道要使用哪个构造函数,从而导致冲突和编译错误。
1.7. 把构造函数放在 private 区域中
一般情况下,构造函数不会写在私有区域,因为这样会阻止类被实例化,但有一种特殊情况,称为单例模式。
A类{
公共:
//获取单例实例的静态方法
静态A getInstance();
//一些公共方法
无效设置(){.}
私人:
//私有构造函数
A();
//私有拷贝构造函数
A(const A rhs);
//其他私有成员.
};
//获取单例实例的静态方法实现
A:getInstance() {
static A a; //静态局部变量。确保它只实例化一次。
返回。
}
//使用单例实例并调用setup方法
A:getInstance().setup();
单例模式:单例模式是一种将类限制为只有一个实例的设计模式,提供全局访问点。单例模式允许一个类只有一个实例并且易于访问。静态方法getInstance:这是单例模式的一个重要方法。可以通过此静态方法访问唯一的实例。在这个方法内部,定义了一个静态局部变量static A a;局部静态变量仅在第一次调用时初始化,因此a 仅创建一次,保证其单例特性。私有构造函数:构造函数被定义为私有。这意味着外部人员无法直接创建该类的实例。这是实现单例模式的关键之一。只有类本身(通过getInstance 方法)可以访问和创建它的实例。私有复制构造函数:复制构造函数也被定义为私有,这样类的实例就不会被复制。由于单例模式需要保证只有一个实例存在,因此还需要防止复制行为。
单例模式通过以下方式确保类只有一个实例:
控制实例化:通过将构造函数设为私有,防止外部代码直接创建实例。提供全局访问点:通过公共静态方法(例如getInstance)提供对唯一实例的访问。复制预防:通过将复制构造函数和赋值运算符设为私有,防止类的实例被复制。
除了单例模式外,构造函数定义在私有区域的情况还有:
工厂模式:使用工厂方法而不是直接使用构造函数来创建类的实例。控制对象的生命周期。例如,您希望仅在某些条件下创建一个对象。
1.8. 常量成员函数
类中的函数可以分为两种类型:修改数据的函数和不修改数据的函数。为了安全起见,所有不修改数据内容的类内成员函数都应该声明为常量成员函数。即double real() const { return re;
如果类中的成员函数没有声明为常量成员函数,则函数调用时会发生冲突。
const 复杂c1(2, 1);
cout c1.real();
cout c2.image();
上面的代码中实例化了一个常量类,并且不能通过调用成员函数来改变变量的内容,但是如果在类中定义成员函数时不加const,就会写成double real() {。 return re; } 和double imag () { return im; } 会“迷惑”编译器并决定是否可以更改,从而导致冲突。
1.9. 参数传递:pass by value vs. pass by reference(to const)
//按值传递(按值传递,传递给自身;慢取决于传递的值的大小)
复杂(双r=0,双i=0): re(r),im(i){}
//按引用传递(按地址传递,非常快,即传递指针的速度,4个字节)
复杂运算符+=(const复杂); //使用const意味着不能在函数内修改复杂变量。
最好通过引用而不是通过值来传递所有参数,但如果不希望对方更改它,请添加const。
1.10. 返回值传递:return by value vs. return by reference(to const)
复合运算符+=(const complex);
double real () const { 返回值}
double imag () const {返回值}
朋友复杂__doapl(complex*, const complex);
上面的代码中,第一行和最后一行代码的返回值都是引用。最好通过引用传递所有返回值,而不是通过值传递,但如果即使传递也不希望对方更改,请添加const。
1.11. friend(友元)
数据通常在私有域中定义(数据封装)。如果您想从外部检索它,则必须通过类中的公共域函数来完成。我希望能够通过允许某些“朋友”功能直接检索私有域数据来定义朋友。
类复合体
{
公共:
复数(双r=0,双i=0)
: Ri(r), Im(i)
{}
复杂(): re(r),im(i){}
复合运算符+=(const complex);
double real () const { 返回值}
double imag () const {返回值}
私人:
杜布利,我。
朋友复杂__doapl(complex*, const complex);
};
内联复合体
__doapl(复数* ths,const复数r)
{
ths-re +=r.re;
ths-im +=r.im;
*返回;
}
虽然通过友元检索数据比类内函数更快,但友元实际上破坏了类的封装,因此尽量不要创建太多友元。
1.12. 相同类(class)的各个对象(object)互为友元(friend)
类复合体
{
公共:
复杂(双=0,双i=0)
: Ri(r), Im(i)
{ }
int func(const 复杂参数)
{返回参数.re + 参数.im;}
私人:
杜布利,我。
};
{
复数c1(2, 1);
复合体c2;
c2.func(c1);
}
1.13. C++编程规范总结
所有数据必须放置在私有域中。参数应尽可能通过引用传递。根据情况,返回值应尽可能通过引用传递。不能在类中通过引用传递。体内函数必须添加const(不修改传递参数和数据的函数)必须添加在构造函数中。传递参数时,一定要使用参数化列表。通过他们。
1.14. class body 外的各种定义
问题:
在什么情况下可以使用引用传递?
调用者的变量可以通过引用传递来修改,只要传递的参数的值不改变。当函数需要修改传递给它的变量时,请使用按引用传递。通过引用传递允许函数直接操作原始变量而不是复制它。避免复制的开销:对于大型或复杂的对象,可以通过传递引用来避免复制对象的开销,提高效率。传递数组:在C++中,数组不能直接按值传递,因此数组通常通过引用或指针传递。
什么情况下可以使用引用返回?
返回的引用必须引用有效的对象,而不是局部变量。当函数返回时,局部变量将被销毁,并且返回对局部变量的引用会导致悬空引用。返回类成员:如果函数返回类的成员,请使用按引用返回来允许修改该成员。允许链接:引用返回允许调用者连续调用函数,包括常见的链接调用。返回容器元素:当返回容器内的元素时,使用按引用返回可以避免复制元素并允许修改元素。
1.15. 操作符重载(operator overloading)
1.15.1. 成员函数
内联复合体
__doapl(complex* ths, const 复杂r)
{
ths-re +=r.re;
ths-im +=r.im;
返回*
}
内联复合体
复杂:运算符+=(const复杂r)
{
返回__dopal(this,r)
}
{
复数c1(2, 1);
复数c2(5);
//c1不变并通过引用传递,c2被修改并通过指针传递。
c2+=c1;
}
1.15.2. 非成员函数
这三个函数的返回值必须是局部对象,所以上面三个函数不能通过引用返回(函数返回后值被清除)。
typename(); 这种语法的优点是不需要命名变量。返回复数(x) + y, imag(y);
临时对象的这种用法很少使用,但在标准库中很常见。
1.16. return by reference 语法分析
我们首先看一下部分代码。
内联复合体
__doapl(complex* ths, const 复杂r)
{
ths-re +=r.re;
ths-im +=r.im;
返回*
}
在这段代码中,函数的返回类型很复杂,是引用类型,但函数实际上返回的是*ths,即ths指针的内容。这看似矛盾,但又确实存在。这种写法是没有问题的。因为发送者不需要知道接收者会以引用的形式收到它。如果以指针的形式传递,发送者必须知道接收者将以引用的形式接收它。指针的格式,即它的声明和返回类型必须一致;都是指针类型。
以上来自网络的#C++编程技巧分享相关内容,仅供大家参考。相关信息请参见官方公告。
原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91550.html