八股总结

四种智能指针

管理指针,避免忘记释放空间造成内存泄露

T* release(); //将内部指针设置为nullptr
void reset(T *ptr = nullptr);// 直接释放内部指针所指向的内存

auto_ptr

auto_ptr<std::string> p1 (new string ("hello")); 
auto_ptr<std::string> p2;
p2 = p1; //auto_ptr 不会报错.

p2剥夺p1的所有权,访问p1会造成内存崩溃

unique_ptr

同一时间内只有一个智能指针可以指向该对象,利于避免资源泄露。

若上例采用unique_ptr,则编译器认为非法,避免了p3不再指向有效数据的问题。

shared_ptr 强引用

shared_ptr实现共享式拥有概念,多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。

可以通过成员函数use_count() 来查看资源的所有者个数,除了可以通过 new 来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr 来构造。当我们调用 release() 时,当前指针会释放资源所有权,计数减一。当计数等于 0 时,资源会被释放。

引用计数是线程安全的,但被管理的对象不一定线程安全。

weak_ptr

#include <memory>
#include <iostream>

struct A;
struct B;

struct A {
  std::shared_ptr<B> b_ptr;
  ~A() { std::cout << "A is deleted\n"; }
};

struct B {
  std::shared_ptr<A> a_ptr;
  ~B() { std::cout << "B is deleted\n"; }
};

int main() {
  {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;
  }

  std::cout << "End of main\n";
}

可以将其中一个shared_ptr转换成weak_ptr

C++中内存分配情况

栈 由编译器管理分配和回收,存放局部变量和函数参数。

堆 程序员管理 手动new malloc delete free

全局/静态存储区 初始化/未初始化区

常量存储区 字符串等

代码区

C++中指针参数传递和引用参数传递

引用参数传递。间接寻址操作到主调函数中的相关变量

符号表上,指针变量的地址就是指针本身的地址,因此指针可以改变其指向的对象,而引用的地址就是其引用的变量的地址,无法改变。

Static

  1. 修饰局部变量,放到静态存储区,生命周期与程序相同,作用域不变!

  2. 修饰全局变量、函数 用 static 对全局变量进行修饰改变了其作用域范围,由原来的整个工程可⻅变成 了本文件可⻅。修饰函数与修饰全局变量类似,改变了作用域。

函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;

模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问。 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷⻉;

类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量。

Const

Type
Definition
Can be changed?
Can it change what it points to?

int*

A pointer to an integer.

Yes

Yes

const int*

A pointer to a constant integer. You can change where the pointer points, but you can't change the value it points to.

No

Yes

int const*

Same as const int*. A pointer to a constant integer.

No

Yes

int* const

A constant pointer to an integer. You can change the value it points to, but you can't change where the pointer points.

Yes

No

int&

A reference to an integer. You can change the value it refers to, but you can't make it refer to a different integer.

Yes

No (referenced object is fixed)

const int&

A reference to a constant integer. You can't change the value it refers to, nor can you make it refer to a different integer.

No

No (referenced object is fixed)

int const&

Same as const int&. A reference to a constant integer.

No

No (referenced object is fixed)

int& const

This is actually incorrect syntax. References themselves can't be const or non-const. This will cause a compiler error.

N/A

N/A

We cannot pass the address of const int to int *.

We can pass the address of const int to const int *.

A const object cannot call non-const member functions.

The idea is that we should by no means change the const value.

局部常量放在栈上,全局变量编译期不分配内存,放在符号表中提高访问效率,字面值常量放在常量区。

重载,重写,重定义

重载 同⼀可访问区内被声明的⼏个具有不同参数列表的同名函数,依赖于 C++函数名字的修饰会将参数加在后⾯,可以是参数类型,个数,顺序的不同。根据参数列表决定调⽤哪个函数,载不关⼼函数的返回类型。

重写 派⽣类中新定义⽗类中除了函数体外完全相同的虚函数,注意被写的函数不能是 static 的,⼀定要是虚函数,且其他⼀定要完全相同。要注意,重写和被重写的函数是在不同的类当中的,重写函数的访问修饰符是可以不同的,尽管 virtual 中是 private 的,派⽣类中写可以改为 public。

重定义(隐藏) 派⽣类新定义⽗类中相同名字的⾮ virtual 函数,参数列表和返回类型都可以不同,即⽗类中除了定义成 virtual 且完全相同的同名函数才不会被派⽣类中的同名函数所隐藏(定义)。

强制类型转换

  1. static_cast:

cppCopy code
// 隐式类型转换
int num = 10;
double result = num;  // 隐式转换

// 显式类型转换
double num2 = 3.14;
int result2 = static_cast<int>(num2);  // 显式转换
  1. dynamic_cast:

cppCopy code
class Base {
  virtual void foo() {}
};

class Derived : public Base {
  void foo() {}
};

int main() {
  Base* basePtr = new Derived();
  
  // 下行转换(派生类指针 -> 基类指针)
  Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);  // 安全转换,返回有效指针

  // 上行转换(基类指针 -> 派生类指针)
  Base* basePtr2 = new Base();
  Derived* derivedPtr2 = dynamic_cast<Derived*>(basePtr2);  // 不安全转换,返回空指针

  delete basePtr;
  delete basePtr2;
  return 0;
}
  1. const_cast:

cppCopy code
void printNumber(const int& num) {
  int& numRef = const_cast<int&>(num);
  numRef = 20;  // 去除const属性,修改了原始值
  std::cout << num << std::endl;  // 输出为 20
}

int main() {
  int num = 10;
  printNumber(num);
  return 0;
}
  1. reinterpret_cast:

cppCopy code
int main() {
  int num = 10;
  
  // 整数转换为指针
  int* ptr = reinterpret_cast<int*>(&num);
  *ptr = 20;  // 修改了num的值

  // 指针转换为整数
  int num2 = reinterpret_cast<int>(ptr);

  return 0;
}

野指针与悬空指针

野指针(wild pointer):就是没有被初始化过的指针。⽤ gcc -Wall 编译, 会出现 used uninitialized警告。

悬空指针:是指针最初指向的内存已经被释放了的⼀种指针。

函数指针

char * fun(char * p) {…} // 函数fun
char * (*pf)(char * p); // 函数指针pf
pf = fun; // 函数指针pf指向函数fun
pf(p); // 通过函数指针pf调⽤函数fun

new/delete, malloc/free的区别

执⾏ new 实际上执⾏两个过程:1.分配未初始化的内存空间(malloc);2.使⽤对象的构造函数对空间进⾏初始化;返回空间的⾸地址。如果在第⼀步分配空间中出现问题,则抛出 std::bad_alloc 异常,或被某个设定的异常处理函数捕获处理;如果在第⼆步构造对象时出现异常,则⾃动调⽤ delete 释放内存。

执⾏ delete 实际上也有两个过程:1. 使⽤析构函数对对象进⾏析构;2.回收内存空间(free)。

Last updated