Effective C++ 11-17

本文探讨了C++中深拷贝的重要性,尤其是在动态内存分配的类中,如何正确实现拷贝构造函数和赋值操作符。强调了初始化列表的使用,基类虚析构函数的必要性,以及在operator=中对所有数据成员赋值并检查给自己赋值的情况。

11.为须要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符。

显然,由于动态内存分配,绝对会有深浅拷贝的问题,要重写拷贝构造函数。使其为深拷贝,才干实现真正意义上的拷贝。这是我理解的关于要声明拷贝构造函数的原因。

而对于赋值操作符,类似的道理。

	A b = a;
	b = a;
对于上述两种形式,上面调用的是复制构造函数,而以下才是 赋值操作符=。

赋值与复制非常类似,缺省的操作都是将类的所有成员进行复制。

深拷贝基本的操作非常easy,对于指针。动态申请一块内存来存放指针指向的数据,每一个指针都指向自己的一块内存,而不是其它人的。


12.尽量使用初始化而不要在构造函数里赋值。

即尽量使用成员初始化列表,而不是使用赋值的方法。

首先对于const成员和引用,仅仅能使用初始化列表来初始化。

其次初始化列表效率更高。由于对象的创建分为两步:数据成员的初始化和运行被调用构造函数体内的动作。即使用赋值之前。先进行了数据成员的初始化,然后才是赋值。所以使用初始化列表效率更高。

但当有大量的固定类型的数据成员要在每一个构造函数中以同样的方式初始化的时候,使用赋值会更加合理一点。


13。初始化列表中成员列出的顺序和它们在类中声明的顺序同样。

这是由于初始化列表的顺序并不影响初始化的顺序。初始化的顺序是有成员在类中声明的顺序决定的。而让其顺序同样是使程序看起来是依照初始化列表的顺序初始化。

而c++不使用初始化列表的顺序的原因是:对象的析构函数是依照 与成员在构造函数中创建的相反的顺序 创建的。

则假设对象不是依照一种固定的顺序来初始化,编译器就要记录下每一个对象成员的初始化顺序。这将带来较大的开销。


14、确定基类有虚析构函数。

通过基类的指针去删除派生类的对象时,基类一定要有虚析构函数,不然 会有不可预測的后果。不使用虚析构函数,仅仅调用基类的析构函数去删除派生类对象,这是无法做到。也是无法确定后果的。

构造函数调用是先基类后派生类,而析构函数的顺序是先派生类后基类。

当一个类不作为基类使用时,使用虚析构函数是一个坏主意。由于虚函数的对象会有一个虚指针指向虚表。会浪费空间来储存这个没有意义的指针。

纯虚函数在虚函数后加 =0 就可以。


15.让operator = 返回 *this 的引用。

= 号能够连接起来。由于其返回值的原因,声明operator=的形式例如以下:

C& C:: operator= (const C&);
其输入和返回都是类对象的引用。以实现连续的赋值操作。

返回值是 = 左边值的引用 即 *this的引用,由于右边即參数是const类型的。
函数的參数是 const类型的原因,是 = 右边的值经过计算会获得一个新的结果,而这个对象要用一个暂时对象来储存,这个暂时对象是const类型的,由于其作为函数的參数。且不能被函数改动。


16.在operator = 中对所有数据成员赋值。

对于深拷贝要自己写一个更加正确的 = 操作。

在涉及继承时。派生类的赋值运算必须处理基类的赋值。

假设重写派生类的赋值运算,就必须同一时候显示的对基类部分进行赋值。

class A{
public:
	int a;
	A(int x):a(x){}
//	A& operator=(const A& x){ a = x.a; return *this;}
};
class B:public A{
public:
	int b;
	B(int x):A(x),b(x){}
	B& operator=(const B&);
};
B& B::operator=(const B& x){
	if(this == &x)return *this;
//	A::operator=(x);//调用基类的赋值函数要如此写
	static_cast<A&>(*this) = x;//也能够强制转换为A类型然后在调用基类的默认赋值函数
	b = x.b;
	return *this;
}
拷贝构造函数也是如此。也要对基类的成员进行复制,仅仅要在成员初始化列表中加入基类就可以。

17.在operator=中检查给自己赋值的情况。

这是基于效率考虑的,在赋值的首部检測是给自己赋值,就马上返回。如16中函数所写的那样。

这里除了类中自己成员的赋值。假设有基类,还要调用基类的赋值函数,会添加开销。

还有一个原因是保证正确性。一个赋值运算符必须首先释放掉一个对象的资源。如有些指针指向了动态申请的空间。则赋值前一般要释放这些资源,然后在指向新的资源(假设在赋值開始。用些暂时的指针来记录之前的所有指针指向的内存,然后在赋值后再将暂时指针指向的内存所有释放。这还是不行,由于对这些指针如p,必须有 p = new p[]...来指向新申请的一块空间,而假设是同一个对象的话,这里就将原来的指针指向一个新的地址,且两者同样了,所以又须要一个新的暂时指针来指向赋值对象的指针的值)。也就是说为了使其给自己赋值,对与每一个指针必须新建两个指针。一个储存左边的对象的原指针,一个储存右边的对象的原指针,这种开销时极大极浪费的,也是没有必要的,为了一些不应该进行的为自己赋值要提前准备大量内存来储存数据,这也是不科学的。

所以最好的解决的方法是检測是否为自己赋值,一般採用检測对象地址是否相等,c++中一般採取这个方案。对于java中。不好的做法是检測对象是否相等。即其所有值是否全然相等,较好的方法是依据对象的id来推断对象是否为同一个对象。






转载于:https://www.cnblogs.com/zhchoutai/p/8449629.html

<think>好的,我现在需要帮助用户解决两个问题:一是解释CFLAGS中的一系列编译选项的含义,特别是当用户将CFLAGS设置为包含多个C++标准的选项时可能存在的问题;二是解决他们在Makefile中遇到的CreateProcess错误,错误信息是“process_begin:CreateProcess(NULL,rm-ftraining*.o*.exe,...)failed.make(e=2):ϵͳҲָļ”。首先,关于CFLAGS的问题。用户给出的CFLAGS包含了多个-std=c++XX选项,比如-std=c++11-std=c++20,还有-Wall、-Werror、-g。我需要指出的是,CFLAGS通常是用于C编译器的选项,而CXXFLAGS才是用于C++编译器的。所以用户可能在Makefile中将C++标准选项错误地放到了CFLAGS中,这可能导致问题,因为CFLAGS会被用于C文件的编译,而C文件不支持C++标准选项。这时候应该建议用户将这些选项移到CXXFLAGS中,并且注意到多个-std选项同时存在会冲突,编译器可能只会应用最后一个,或者报错。需要解释每个选项的作用,比如-std指定标准,-Wall开启警告,-Werror将警告视为错误,-g生成调试信息。同时提醒用户避免在CFLAGS中使用C++特定的选项,而应使用CXXFLAGS。接下来是CreateProcess错误。错误信息显示在执行rm命令时失败,可能的原因是命令路径问题、权限问题或者Makefile中的语法错误。用户可能在Makefile中直接使用了rm命令,而Windows系统默认没有rm命令,除非使用Cygwin或MinGW等环境。需要建议用户检查是否在Windows下使用了Unix命令,如果是,可以替换为del命令,或者确保环境正确配置。另外,检查命令中的通配符是否正确转义,路径是否有空格需要引号包裹。还可能涉及到Makefile中的clean目标是否正确编写,比如是否使用了正确的命令和变量。结合用户提供的引用资料,引用[1]中用户修改了Makefile,将LINK从CC改为CXX,这可能涉及到使用g++作为链接器,但同时也需要确保CXXFLAGS正确设置。引用[2]提到在configure时传递CFLAGS和CXXFLAGS的问题,用户可能在此过程中错误地设置了参数,导致生成错误的Makefile。引用[4]指出C和C++文件分别使用CFLAGS和CXXFLAGS,因此用户需要将C++选项放在CXXFLAGS中。在回答时,需要结构清晰地分点解释CFLAGS各个选项,指出潜在的问题,并给出修正建议。然后针对CreateProcess错误,列出可能的原因和解决方案,比如检查命令拼写、路径处理、环境变量等,并结合用户之前的问题中的解决方案,比如使用完整路径或引号包裹路径。最后生成相关问题,引导用户进一步解决可能遇到的问题,比如如何正确设置C和C++编译选项,如何在Windows下处理Makefile中的命令,以及混合编译C/C++时的注意事项等。</think> ### 一、CFLAGS编译选项解析 #### 用户设置的编译选项: ```makefile CFLAGS=-std=c++11 -std=c++14 -std=c++17 -std=c++20 -Wall -Werror -g ``` #### 选项含义及潜在问题: 1. **-std=系列参数** - `-std=c++11` 到 `-std=c++20` 分别表示启用C++11C++20标准 - **冲突问题**:多个`-std`参数同时存在时,编译器仅接受最后一个参数(如`-std=c++20`会覆盖其他标准)[^4] - **语法错误**:`CFLAGS`是C编译器选项,而`CXXFLAGS`才是C++编译器选项。应将所有C++标准参数移到`CXXFLAGS`中 2. **警告与错误控制** - `-Wall`:启用所有警告 - `-Werror`:将警告视为错误(编译失败) - $$ \text{警告级别} \propto \frac{1}{\text{代码质量}} \quad \text{(警告越少,代码质量越高)} $$ [^1] 3. **调试信息** - `-g`:生成调试符号(GDB调试必需) #### 正确配置示例: ```makefile # C编译器选项(仅处理.c文件) CFLAGS = -Wall -Werror -g # C++编译器选项(处理.cpp文件) CXXFLAGS = -std=c++20 -Wall -Werror -g ``` ### 二、CreateProcess错误解决方案 #### 错误特征: ```bash process_begin: CreateProcess(NULL, rm -f training *.o *.exe, ...) failed. make (e=2): ϵͳҲָļ ``` #### 错误原因与修复: | 错误类型 | 检测方法 | 解决方案 | |-----------------|----------------------------|--------------------------------------------------------------------------| | **Unix命令冲突** | `where rm` | Windows系统需使用`del`代替`rm`:<br>`clean: del *.o *.exe` | | **通配符失效** | `echo *.o` | 使用`foreach`函数展开通配符:<br>`clean: $(foreach f,*.o *.exe,del $(f))` | | **路径空格** | `echo "%PATH%"` | 用双引号包裹含空格路径:<br>`CXX = "C:/Program Files/gcc/bin/g++.exe"` | | **权限不足** | `icacls .` | 以管理员身份运行CMD:<br>`runas /user:Administrator "make clean"` | #### 完整修复案例: ```makefile # Windows系统Makefile修正 clean: powershell -Command "Remove-Item *.o, *.exe, training -ErrorAction Ignore" ``` ### 三、数学形式化表达 编译选项的优先级可通过集合运算描述: $$ \text{Effective\_Options} = \bigcup_{i=1}^{n} \left( \text{Option}_i \right) - \left\{ \text{Conflicting\_Options} \right\} $$ 其中`n`为参数数量,冲突选项的消除遵循**最后生效原则**。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值