可以通过查看函数名是否经过名称修饰来初步判断一段程序是由C编译器还是由C++编译器编译的。
在C语言中,函数名不会经过名称修饰。而在C++中,函数名会根据函数的参数类型、个数和顺序等信息进行名称修饰(名称 mangling)。
例如,下面是一个在C中的函数声明:
void foo(int a);
对应的汇编代码如下:
foo:
push ebp
mov ebp, esp
sub esp, 8
mov DWORD PTR [ebp-4], 0
mov eax, DWORD PTR [ebp+8]
add eax, 3
mov DWORD PTR [ebp-4], eax
nop
leave
ret
而在C++中,该函数的声明可能会变成这样:
extern "C" void __cdecl foo(int);
其中,__cdecl
关键字表示函数调用采用C标准的cdecl调用规则,而extern "C"
则表示以C语言方式进行符号导出,避免C++的名称修饰。
因此,如果你看到了函数名经过了名称修饰,那么这段程序就很有可能是由C++编译器编译的。当然,并不是所有的C++编译器都会进行名称修饰,也不是所有的用到过C语言的库都不会进行名称修饰,这只是一个初步的判断方法。
函数重载的原理是利用了C++的名称 mangling 和函数签名来实现。C++编译器根据函数的参数类型、个数和顺序等信息,为每个函数生成一个唯一的符号名,这个过程就是名称 mangling(名称修饰)。
例如,对于下面的两个函数:
void foo(int a);
void foo(double b);
编译器会分别为它们生成不同的符号名,如_Z3fooi
和_Z3food
。在调用这些函数时,编译器会根据函数的名称和参数列表来选择正确的函数进行调用。
函数重载的好处是可以让程序员使用相同的函数名来表示不同含义的函数,从而使代码更加简洁易懂、易维护和可重用。
c++进行函数重载的实现原理叫做名字改编(name mangling),具体规则是
1.函数名必须相同
2.参数列表必须不同(参数的类型不同,个数不同,顺序不同)任意一项不同即可
3.函数的返回值类型可以相同也可以不相同
4.仅仅返回类型不同不足以成为函数的重载
inline
函数与带参数的宏定义的区别?内联函数在编译时展开,宏在预编译时展开;
内联函数直接潜逃到目标代码中,宏时简单的做文本替换
内联函数由类型检测语法判断等功能,而宏没有
inline函数时函数,宏不是;
宏定义时要注意书写(参数要括起来)否则容易出现歧义,内联函数不会产生歧义
std::string
与const char *
有什么区别?string是类,const char*是指针,一个是字符串对象,一个是c风格的字符串
string可以看作是对const char*的封装,string有完善的接口
空类的空间大小是1
为什么会占据1而不是0?
因为类需要有不同的地址表示,每个类的地址独一无二
c++一个类中6个默认的函数分别是构造,拷贝构造,赋值运算符重载,析构,以及取地址操作符重载以及const修饰的取地址操作符重载
将a的值拷贝给b
拷贝构造函数是一种特殊的构造函数,用于创建一个新对象并将其初始化为同类型的另一个对象。它通常在以下场景中被调用:
将一个对象作为参数传递给函数时,会调用该对象的拷贝构造函数来创建一个新对象。
在函数返回时,如果返回值为对象类型,则会调用该对象的拷贝构造函数来创建一个新对象。
拷贝构造函数的形态如下:
class MyClass {
public:
// 拷贝构造函数
MyClass(const MyClass& other) {
// 执行拷贝操作,将other的成员变量复制到this指向的对象中
}
};
注意,在拷贝构造函数中,参数应该使用 const 引用来避免对原始对象进行修改,因为拷贝构造函数的目的就是创建一个新的对象,而不是更改原始对象。因此,拷贝构造函数的参数不能被修改。
赋值运算符函数(operator=)是一种用于将一个对象的值赋给另一个对象的特殊函数。
其形态如下:
class MyClass {
public:
// 赋值运算符函数
MyClass& operator=(const MyClass& other) {
if (this != &other) { // 避免自我赋值
// 执行赋值操作,将 other 的成员变量复制到 this 指向的对象中
}
return *this;
}
};
需要注意的是,赋值运算符函数必须返回一个指向当前对象的引用,并且参数应该使用 const 引用来避免对原始对象进行修改。
通常情况下,如果不手动提供赋值运算符函数,编译器会自动生成一个默认的赋值运算符函数。然而,在以下情况下需要手动提供赋值运算符函数:
需要注意的是,在手动实现赋值运算符时,应该遵循“异常安全性”原则,即在执行赋值操作时应该保证异常发生时不会破坏对象的状态。可以使用 copy-and-swap 技术或者自定义异常处理机制来实现异常安全的赋值运算符函数。
**浅拷贝(Shallow Copy)和深拷贝(Deep Copy)**都是复制对象的方式,但它们的实现方式和效果有所不同。
浅拷贝是指仅仅复制对象中的成员变量,而不会复制成员变量所指向的内存。这意味着源对象和目标对象将共享同一块内存空间,当其中一个对象修改了内存中的值时,另一个对象也会随之改变。浅拷贝通常通过默认的拷贝构造函数和赋值运算符函数来实现。
深拷贝是指在复制对象时,不仅要复制对象本身的成员变量,还要复制成员变量所指向的内存。这样,源对象和目标对象将拥有各自独立的内存空间,互不影响。深拷贝需要手动实现拷贝构造函数和赋值运算符函数,并且需要注意释放新分配的内存空间,以避免内存泄漏。
需要注意的是,在进行深拷贝时,如果成员变量中包含了指向其他对象或资源的指针,则必须为该指针所指向的对象或资源进行复制或引用计数管理,否则可能会出现内存泄漏或者悬空指针等问题。
综上所述,浅拷贝和深拷贝的区别在于是否复制指针所指向的内容。通常情况下,需要根据具体场景来选择使用浅拷贝还是深拷贝。
浅拷贝:只是增加一个指针指向已存在的内存地址
深拷贝:增建了一个指针并申请了一个新的内存,使这个增加的指针指向这个新的内存
浅拷贝仅仅使指向被复制的内存地址,原地址发生改变,那么浅复制出来的对象也会相应的改变。深复制在计算机中开辟了一块新的内存地址用于存放复制的对象。