new是C++的关键字,用于动态分配内存并创建对象。它可以根据类型自动计算所需内存空间,并调用对象的构造函数进行初始化。在使用new分配内存后,需要使用delete来释放这些内存空间,以防止内存泄漏。
malloc是C语言的库函数,用于动态分配一块指定大小的内存块,并返回其地址。需要注意的是,使用malloc分配内存后,需要使用free来释放这些内存空间,以防止内存泄漏。
语法:new是C++的关键字,而malloc是C语言的库函数。
类型安全:new操作符会根据类型自动计算所需内存大小,并进行类型匹配,返回的是对象类型指针;而malloc需要手动计算内存大小,并使用强制类型转换,返回的是void指针。
构造函数与析构函数的调用:new会自动调用对象的构造函数进行初始化,而malloc不会调用构造函数,得到的内存空间内容是未初始化的。
内存泄漏的检测:new可以通过异常机制检测内存分配失败,而malloc在分配失败时返回NULL,需要手动检查。
重载和自定义类型:new操作符可以重载,并能够与自定义类型的构造函数和析构函数配合使用;而malloc是库函数,不会调用自定义类型的构造和析构函数
#include <iostream> #include <cstdlib> int main() { // 使用new进行动态内存分配和释放 int* newPtr = new int(10); std::cout << "Value allocated with new: " << *newPtr << std::endl; delete newPtr; // 使用malloc进行内存分配和释放 int* mallocPtr = (int*)malloc(sizeof(int)); if (mallocPtr != nullptr) { *mallocPtr = 20; std::cout << "Value allocated with malloc: " << *mallocPtr << std::endl; free(mallocPtr); } return 0; }
char c = 'A'; int i = c; // 将字符'A'的ASCII码值赋给i
2.将int转换为char:可以使用强制类型转换 (static_cast<char>) 将int类型的变量转换为char类型的变量,该方法只会截取int变量的低位字节作为字符。
int i = 65; char c = static_cast<char>(i); // 将整数65转换为对应的字符'A'
需要注意的是,对于转换为char的int值,如果超出了char类型的范围(-128至127),将会发生溢出,只保留最低位字节的值。
野指针(Dangling Pointer):野指针是指指向已释放或无效的内存的指针。当指针指向的内存被释放后,该指针仍然保留着原来的地址,但是指向的内存已经无效。对野指针进行解引用操作或者修改指针指向的内存可能导致程序崩溃或产生未定义的行为。
举例:
int* getDanglingPointer() { int* ptr = new int(5); // 动态分配内存 delete ptr; // 释放内存 return ptr; // 返回野指针 } int main() { int* danglingPtr = getDanglingPointer(); // 在此时,danglingPtr 是一个野指针 // 对野指针进行解引用或操作将导致未定义的行为 *danglingPtr = 10; // 可能导致程序崩溃 return 0; }
悬挂指针(Dangling Reference):悬挂指针是指引用了已被销毁或无效的对象的引用。当引用的对象被销毁后,悬挂指针仍然保留着之前的引用,但是该对象已经不存在。对悬挂指针进行访问或操作可能导致未定义的行为。
示例代码:
int& getDanglingReference() { int x = 5; int& ref = x; // 创建一个引用,引用了局部变量 return ref; // 返回悬挂指针 } int main() { int& danglingRef = getDanglingReference(); // 在此时,danglingRef 是一个悬挂指针 // 对悬挂指针进行访问或操作将导致未定义的行为 danglingRef = 10; // 可能导致不正确的结果 return 0; }
类型不同:NULL是宏定义或整数值0,而nullptr是C++11引入的关键字,表示空指针。
安全性不同:NULL可能导致函数调用二义性问题,nullptr更安全,不会被错误解释为整型。
上下文匹配不同:NULL可以用于整型类型的上下文,nullptr只能用于指针类型的上下文。
智能指针是C++中用于管理动态分配对象的一种特殊指针类型,它能够自动地分配和释放内存,避免内存泄漏和悬挂指针的问题。常用的智能指针有unique_ptr
、shared_ptr
和weak_ptr
和auto_ptr
(已弃用)。
unique_ptr
4.unique指针规定一个智能指针独占一块内存资源。当两个智能指针同时指向一块内存,编译报错。
举例代码:
#include <iostream> #include <memory> int main() { std::unique_ptr<int> uniquePtr(new int(10)); if (uniquePtr) { std::cout << *uniquePtr << std::endl; // 输出10 } uniquePtr.reset(); // 手动释放内存 return 0; }
shared_ptr:
举例代码:
#include <iostream> #include <memory> int main() { std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(10); std::shared_ptr<int> sharedPtr2 = sharedPtr1; std::cout << *sharedPtr1 << " " << *sharedPtr2 << std::endl; // 输出10 10 sharedPtr1.reset(); // 释放sharedPtr1所指向的对象 if (sharedPtr2) { std::cout << *sharedPtr2 << std::endl; // 输出10 } return 0; }
weak_ptr:
举例代码:
#include <iostream> #include <memory> int main() { std::shared_ptr<int> sharedPtr = std::make_shared<int>(10); std::weak_ptr<int> weakPtr(sharedPtr); if (auto lockedPtr = weakPtr.lock()) { std::cout << *lockedPtr << std::endl; // 输出10 } sharedPtr.reset(); // 释放sharedPtr,引用计数为0 if (weakPtr.expired()) { std::cout << "Weak pointer expired" << std::endl; } return 0; }
C++中有四种类型转换符可用于在不同类型之间进行类型转换。static_cast、dynamic_cast、const_cast和reinterpret_cast。
static_cast:
示例:
int num = 10; double convertedNum = static_cast<double>(num); class Base {}; class Derived : public Base {}; Base* basePtr = new Derived(); Derived* derivedPtr = static_cast<Derived*>(basePtr);
dynamic_cast:
抛出std::bad_cast异常(对于引用转换)
示例:
class Base { virtual void foo() {} }; class Derived : public Base {}; Base* basePtr = new Derived(); Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); if (derivedPtr) { // 转换成功 }
const_cast:
示例:
const int num = 10; int* nonConstPtr = const_cast<int*>(&num); *nonConstPtr = 20; // 合法:修改nonConstPtr的值
reinterpret_cast:
示例代码:
int num = 10; double* doublePtr = reinterpret_cast<double*>(&num); // 不安全,可能导致未定义行为 int* intPtr = reinterpret_cast<int*>(doublePtr); // 转回原始类型
重载(Overloading):
举例代码:
#include <iostream> void printNumber(int num) { std::cout << "Integer number: " << num << std::endl; } void printNumber(double num) { std::cout << "Floating-point number: " << num << std::endl; } int main() { printNumber(10); printNumber(3.14); return 0; }
重写(Override):
举例代码:
#include <iostream> class Base { public: virtual void sayHello() { std::cout << "Hello from Base class!" << std::endl; } }; class Derived : public Base { public: void sayHello() override { // 使用 override 关键字表明重写了父类的函数 std::cout << "Hello from Derived class!" << std::endl; } }; int main() { Base* basePtr = new Derived(); basePtr->sayHello(); // Output: "Hello from Derived class!" delete basePtr; return 0; }
隐藏(Hiding):
举例代码:
#include <iostream> class Base { public: void sayHello() { std::cout << "Hello from Base class!" << std::endl; } }; class Derived : public Base { public: void sayHello() { std::cout << "Hello from Derived class!" << std::endl; } }; int main() { Base baseObj; Derived derivedObj; baseObj.sayHello(); // Output: "Hello from Base class!" derivedObj.sayHello(); // Output: "Hello from Derived class!" Base* basePtr = new Derived(); basePtr->sayHello(); // Output: "Hello from Base class!" delete basePtr; return 0; }
面向对象编程(OOP)的三大特性是封装、继承和多态。下面对每个特性进行简要说明:
封装(Encapsulation):
继承(Inheritance):
多态(Polymorphism):
利用虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员。 基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态,或者说有多种表现方式,我们将这种现象称为多态(Polymorphism)。
代码举例:
#include <iostream> class Base { public: virtual void print() { std::cout << "This is the Base class" << std::endl; } }; class Derived : public Base { public: void print() override { std::cout << "This is the Derived class" << std::endl; } }; int main() { Base* basePtr; Base baseObj; Derived derivedObj; basePtr = &baseObj; basePtr->print(); // 此时使用基类的成员函数来打印消息 basePtr = &derivedObj; basePtr->print(); // 此时使用派生类的成员函数来打印消息 return 0; }#晒一晒我的offer##24届软开秋招面试经验大赏##我发现了面试通关密码##如何判断面试是否凉了#