文章目录

  • 内存分区模型
  • new操作符
  • 引用
    • 引用做变量参数
    • 引用做函数参数
    • 基本语法
    • 注意事项
  • 类和对象
    • 封装
      • 属性和行为作用
      • 访问权限
      • struct和class区别
      • 成员属性设置为私有
    • 对象特性
      • 构造函数和析构函数
      • 构造函数的分类及调用
      • 拷贝构造函数调用的时机
      • 静态成员函数
      • this指针
    • 友元
    • 运算符重载
      • 加号运算符 +
      • 左移运算符 <<
      • 赋值运算符重载
      • 关系运算符重载
    • 继承
      • 继承的基本语法
      • 继承方式
      • 继承中构造和析构顺序
      • 继承同名成员处理方式
      • 继承同名静态成员处理方式
      • 多继承语法
      • 菱形继承
    • 多态
  • 洗牌算法:
  • 构造函数可以是虚函数吗,为什么
  • new和malloc的区别
  • delete和delete[]
  • c++中四种类型cast(强制)转换

内存分区模型

  • 代码区:存放函数体的二进制代码,有操作系统进行管理
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程


new操作符

int* p = new int(10);
delete p;int * arr = new int[10];
delete[] arr;

引用

引用做变量参数

作用:给变量起别名
语法:数据类型 &别名 = 原名
注意事项:引用必须初始化,引用在初始化后,不可以改变

int a = 10;
int &b = a;

引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参

int mySwap(int &a,int &b){int tmp = a;a = b;b = tmp;
}mySwap(a,b); //引用传递,形参会修饰实参

##函数重载

基本语法

作用:函数名可以相同,提高复用性
满足的条件:

  • 同一个作用域下
  • 函数名称相同
  • 函数参数类型不同或者个数不同或者顺序不同
void func() {cout << "func 的调用!" << endl;
}
void func(int a) {cout << "func(int a) 的调用!" << endl;
}
void func(double a) {cout << "func(double a) 的调用!" << endl;
}
void func(int a, double b) {cout << "func(int a,double b) 的调用!" << endl;
}
void func(double a, int b) {cout << "func(double a,int b) 的调用!" << endl;
}

注意事项

引用作为重载的条件

void func(int &a) {  //int &a = 10 不合法cout << "func(int &a) 的调用!" << endl;
}
void func(const int &a) {   //const int &a = 10 合法cout << "func(const int &a) 的调用!" << endl;
}int main() {int a = 10;func(a);   //"func(int &a) 的调用!"func(10);    //"func(const int &a) 的调用!"
}

函数重载碰到默认参数

void func(int a,int b = 10) {   cout << "func(int &a) 的调用!" << endl;
}
void func(int a) {  cout << "func(const int &a) 的调用!" << endl;
}int main() {int a = 10;func(10); //出现二义性,报错
}

类和对象

C++面向对象的三大特性:封装、继承、多态

封装

属性和行为作用

意义

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制
const double PI = 3.14;
class Circle {//访问权限
public:int m_r;double calculateZC() {return 2 * PI * m_r;}
};int main() {//实例化 通过一个实例创建一个具体的对象Circle cl;   //通过类创建具体的圆(对象)cl.m_r = 10;cout << "周长" << cl.calculateZC() << endl;
}

访问权限

类型 权限 成员 类内 类外
public 公共权限 成员 类内可以访问 类外可以访问
protected 保护权限 成员 类内可以访问 类外不可以访问 儿子可以访问父亲中的保护内容
private 私有权限 成员 类内可以访问 类外不可以访问 儿子不可以访问父亲中的私有内容
class Person {
public:string m_Name;protected:string m_Car;private:int m_Password;public:void func() {m_Name = "mike";m_Car = "mini";m_Password = 123456;}
};int main() {//实例化具体对象Person p1;p1.m_Name = "zhang san";
}

struct和class区别

默认权限不同

  • struct 默认权限是公共 public
  • class 默认权限是私有 private

成员属性设置为私有

优点

  • 将所有成员属性设置为私有,可以自己控制读写权限
  • 对于写权限,我们可以检测数据的有效性
class Person {
public://写姓名void setName(string name) {m_Name = name;}//读姓名string getName() {return m_Name;}//获取年龄int getAge() {m_Age = 12;return m_Age;}void setAge(int age) {if (age < 0 || age > 150) {cout << "error" << endl;return;}m_Age = age;}//设置情人void setLover(string lover) {m_Lover = lover;}
private://可读可写string m_Name;//可读可写int m_Age;//只写string m_Lover;
};int main() {Person p;p.setName("zhang san");p.setLover("bb");cout << p.getName() << endl;cout << p.getAge() << endl;}
class Cube {
public:void setL(int l) {m_L = l;}int getL() {return m_L;}void setW(int w) {m_W = w;}int getW() {return m_W;}void setH(int h) {m_H = h;}int getH() {return m_H;}bool isSameByClass(Cube& c) {if (m_L == c.getL() && m_W == c.getW() && m_H == c.getH())return true;elsereturn false;}private:int m_L;int m_W;int m_H;
};

point.h

#pragma onceclass Point {
private:int m_X;int m_Y;public:void setX(int x);int getX();void setY(int y);int getY();
};

point.cpp

#include "point.h"void Point::setX(int x) {m_X = x;
}
int Point::getX() {return m_X;
}
void Point::setY(int y) {m_Y = y;
}
int Point::getY() {return m_Y;
}

circle.h

#pragma once
#include "point.h"class Circle {
private:int m_R;Point m_Center;
public:void setR(int r);int getR();void setCenter(Point center);Point getCenter();
};

circle.cpp

#include "circle.h"
void Circle::setR(int r) {m_R = r;
}
int Circle::getR() {return m_R;
}
void Circle::setCenter(Point center) {m_Center = center;
}
Point Circle::getCenter() {return m_Center;
}

对象特性

构造函数和析构函数

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

构造函数语法: 类名(){}

  1. 没有返回值也不写void
  2. 函数名称与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 调用对象时自动调用构造,无须手动调用,且只需调用一次

析构函数语法: ~类名(){}

  1. 没有返回值也不写void
  2. 函数名称与类名相同,在名称前加上符号~
  3. 析构函数不可以有参数,因此不可以发生重载
  4. 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
class Person {
public://1.构造函数 初始化Person() {cout << "Person 构造函数的调用" << endl;}~Person() {cout << "Person 析构函数的调用" << endl;}
};void test01() {Person p;
}
int main() {test01();   //Person 构造函数的调用//Person 析构函数的调用Person p;system("pause"); //Person 构造函数的调用return 0;
}

构造函数的分类及调用

分类方法

  • 按参数分为:有参构造和无参构造
  • 按类型分为:普通构造和拷贝构造

调用方法

  • 括号法
  • 显示法
  • 隐式转换法
class Person {
public://构造函数Person() {cout << "Person 无参构造函数的调用" << endl;}Person(int a) {cout << "Person 有参构造函数的调用" << endl;}//拷贝构造函数Person(const Person &p) {age = p.age;cout << "Person 拷贝构造函数的调用" << endl;}int age;
};void test01() {//括号法//Person p1;  //默认,不加()//Person p2(10);   //有参//Person p3(p2);    //拷贝//显示法//Person p1;//Person p2 = Person(10);//Person p3 = Person(p2);//Person(10);  //匿名对象 特点:当前行执行结束后,系统会立即回收匿名对象//隐式转换法Person p4 = 10; //相当于 Person p4 = Person(10);Person P5 = p4;
}
int main() {test01();
}

拷贝构造函数调用的时机

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 值方式返回局部对象

静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量
class Person {
public:static void func() {m_A = 100;cout << "static void func 调用" << endl;}static int m_A;//静态成员函数也是有访问权限的
private:static void func2() {cout << "static void func2调用" << endl;}
};int Person::m_A = 0;void test01() {//通过对象访问Person p;p.func();//通过类名访问Person::func();//Person::func2();   //报错,私有权限访问不到
}

this指针

this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
用途

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this
class Person{
public:Person(int age){//1、当形参和成员变量同名时,可用this指针来区分this->age = age;}Person& PersonAddPerson(Person p){this->age += p.age;//返回对象本身return *this;}int age;
};void test01(){Person p1(10);cout << "p1.age = " << p1.age << endl;Person p2(10);p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);cout << "p2.age = " << p2.age << endl;
}

友元

  • 成员函数做友元
  • 类做友元
  • 成员函数做友元
//成员函数做友元
class Building
{//告诉编译器 goodGay全局函数 是 Building类的好朋友,可以访问类中的私有内容friend void goodGay(Building * building);public:Building(){this->m_SittingRoom = "客厅";this->m_BedRoom = "卧室";}public:string m_SittingRoom; //客厅private:string m_BedRoom; //卧室
};void goodGay(Building * building)
{cout << "好基友正在访问: " << building->m_SittingRoom << endl;cout << "好基友正在访问: " << building->m_BedRoom << endl;
}void test01()
{Building b;goodGay(&b);
}int main(){test01();system("pause");return 0;
}
//类做友元
class Building;
class goodGay
{
public:goodGay();void visit();private:Building *building;
};class Building
{//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容friend class goodGay;public:Building();public:string m_SittingRoom; //客厅
private:string m_BedRoom;//卧室
};Building::Building()
{this->m_SittingRoom = "客厅";this->m_BedRoom = "卧室";
}goodGay::goodGay()
{building = new Building;
}void goodGay::visit()
{cout << "好基友正在访问" << building->m_SittingRoom << endl;cout << "好基友正在访问" << building->m_BedRoom << endl;
}void test01()
{goodGay gg;gg.visit();}int main(){test01();system("pause");return 0;
}
//成员函数做友元
class Building;
class goodGay
{
public:goodGay();void visit(); //只让visit函数作为Building的好朋友,可以发访问Building中私有内容void visit2(); private:Building *building;
};class Building
{//告诉编译器  goodGay类中的visit成员函数 是Building好朋友,可以访问私有内容friend void goodGay::visit();public:Building();public:string m_SittingRoom; //客厅
private:string m_BedRoom;//卧室
};Building::Building()
{this->m_SittingRoom = "客厅";this->m_BedRoom = "卧室";
}goodGay::goodGay()
{building = new Building;
}void goodGay::visit()
{cout << "好基友正在访问" << building->m_SittingRoom << endl;cout << "好基友正在访问" << building->m_BedRoom << endl;
}void goodGay::visit2()
{cout << "好基友正在访问" << building->m_SittingRoom << endl;//cout << "好基友正在访问" << building->m_BedRoom << endl;
}void test01()
{goodGay  gg;gg.visit();}int main(){test01();system("pause");return 0;
}

运算符重载

加号运算符 +

  • 对于内置的数据类型的表达式的的运算符是不可能改变的

  • 不要滥用运算符重载

class Person {
public:Person() {};Person(int a, int b){this->m_A = a;this->m_B = b;}//成员函数实现 + 号运算符重载Person operator+(const Person& p) {Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}public:int m_A;int m_B;
};//全局函数实现 + 号运算符重载
//Person operator+(const Person& p1, const Person& p2) {
//  Person temp(0, 0);
//  temp.m_A = p1.m_A + p2.m_A;
//  temp.m_B = p1.m_B + p2.m_B;
//  return temp;
//}//运算符重载 可以发生函数重载
Person operator+(const Person& p2, int val)
{Person temp;temp.m_A = p2.m_A + val;temp.m_B = p2.m_B + val;return temp;
}void test() {Person p1(10, 10);Person p2(20, 20);//成员函数方式Person p3 = p2 + p1;  //相当于 p2.operaor+(p1)cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl;Person p4 = p3 + 10; //相当于 operator+(p3,10)cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl;}int main() {test();system("pause");return 0;
}

左移运算符 <<

class Person {friend ostream& operator<<(ostream& out, Person& p);public:Person(int a, int b){this->m_A = a;this->m_B = b;}//成员函数 实现不了  p << cout 不是我们想要的效果//void operator<<(Person& p){//}private:int m_A;int m_B;
};//全局函数实现左移重载
//ostream对象只能有一个
ostream& operator<<(ostream& out, Person& p) {out << "a:" << p.m_A << " b:" << p.m_B;return out;
}void test() {Person p1(10, 20);cout << p1 << "hello world" << endl; //链式编程
}int main() {test();system("pause");return 0;
}

赋值运算符重载

c++编译器至少给一个类添加4个函数

  • 默认构造函数(无参,函数体为空)

  • 默认析构函数(无参,函数体为空)

  • 默认拷贝构造函数,对属性进行值拷贝

  • 赋值运算符 operator=, 对属性进行值拷贝

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

class Person
{
public:Person(int age){//将年龄数据开辟到堆区m_Age = new int(age);}//重载赋值运算符 Person& operator=(Person &p){if (m_Age != NULL){delete m_Age;m_Age = NULL;}//编译器提供的代码是浅拷贝//m_Age = p.m_Age;//提供深拷贝 解决浅拷贝的问题m_Age = new int(*p.m_Age);//返回自身return *this;}~Person(){if (m_Age != NULL){delete m_Age;m_Age = NULL;}}//年龄的指针int *m_Age;};void test01()
{Person p1(18);Person p2(20);Person p3(30);p3 = p2 = p1; //赋值操作cout << "p1的年龄为:" << *p1.m_Age << endl;cout << "p2的年龄为:" << *p2.m_Age << endl;cout << "p3的年龄为:" << *p3.m_Age << endl;
}int main() {test01();//int a = 10;//int b = 20;//int c = 30;//c = b = a;//cout << "a = " << a << endl;//cout << "b = " << b << endl;//cout << "c = " << c << endl;system("pause");return 0;
}

关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;};bool operator==(Person & p){if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return true;}else{return false;}}bool operator!=(Person & p){if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return false;}else{return true;}}string m_Name;int m_Age;
};void test01()
{//int a = 0;//int b = 0;Person a("孙悟空", 18);Person b("孙悟空", 18);if (a == b){cout << "a和b相等" << endl;}else{cout << "a和b不相等" << endl;}if (a != b){cout << "a和b不相等" << endl;}else{cout << "a和b相等" << endl;}
}int main() {test01();system("pause");return 0;
}

继承

继承的基本语法

//公共页面
class BasePage
{
public:void header(){cout << "首页、公开课、登录、注册...(公共头部)" << endl;}void footer(){cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;}void left(){cout << "Java,Python,C++...(公共分类列表)" << endl;}};//Java页面
class Java : public BasePage
{
public:void content(){cout << "JAVA学科视频" << endl;}
};
//Python页面
class Python : public BasePage
{
public:void content(){cout << "Python学科视频" << endl;}
};
//C++页面
class CPP : public BasePage
{
public:void content(){cout << "C++学科视频" << endl;}
};void test01()
{//Java页面cout << "Java下载视频页面如下: " << endl;Java ja;ja.header();ja.footer();ja.left();ja.content();cout << "--------------------" << endl;//Python页面cout << "Python下载视频页面如下: " << endl;Python py;py.header();py.footer();py.left();py.content();cout << "--------------------" << endl;//C++页面cout << "C++下载视频页面如下: " << endl;CPP cp;cp.header();cp.footer();cp.left();cp.content();}int main() {test01();system("pause");return 0;
}

总结:

继承的好处:可以减少重复的代码

class A : public B;

A 类称为子类 或 派生类

B 类称为父类 或 基类

派生类中的成员,包含两大部分

一类是从基类继承过来的,一类是自己增加的成员。

从基类继承过过来的表现其共性,而新增的成员体现了其个性。

继承方式

继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承
class Base1
{
public: int m_A;
protected:int m_B;
private:int m_C;
};//公共继承
class Son1 :public Base1
{
public:void func(){m_A; //可访问 public权限m_B; //可访问 protected权限//m_C; //不可访问}
};void myClass()
{Son1 s1;s1.m_A; //其他类只能访问到公共权限
}//保护继承
class Base2
{
public:int m_A;
protected:int m_B;
private:int m_C;
};
class Son2:protected Base2
{
public:void func(){m_A; //可访问 protected权限m_B; //可访问 protected权限//m_C; //不可访问}
};
void myClass2()
{Son2 s;//s.m_A; //不可访问
}//私有继承
class Base3
{
public:int m_A;
protected:int m_B;
private:int m_C;
};
class Son3:private Base3
{
public:void func(){m_A; //可访问 private权限m_B; //可访问 private权限//m_C; //不可访问}
};
class GrandSon3 :public Son3
{
public:void func(){//Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到//m_A;//m_B;//m_C;}
};
访问权限 public protected private
对本类 可见 可见 可见
对子类 可见 可见 不可见
对外部(调用方) 可见 不可见 不可见

继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数

问题:父类和子类的构造和析构顺序是谁先谁后?

class Base
{
public:Base(){cout << "Base构造函数!" << endl;}~Base(){cout << "Base析构函数!" << endl;}
};class Son : public Base
{
public:Son(){cout << "Son构造函数!" << endl;}~Son(){cout << "Son析构函数!" << endl;}};void test01()
{//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反Son s;
}int main() {test01();system("pause");return 0;
}

总结:继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

继承同名成员处理方式

问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
class Base {
public:Base(){m_A = 100;}void func(){cout << "Base - func()调用" << endl;}void func(int a){cout << "Base - func(int a)调用" << endl;}public:int m_A;
};class Son : public Base {
public:Son(){m_A = 200;}//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域void func(){cout << "Son - func()调用" << endl;}
public:int m_A;
};void test01()
{Son s;cout << "Son下的m_A = " << s.m_A << endl;cout << "Base下的m_A = " << s.Base::m_A << endl;s.func();s.Base::func();s.Base::func(10);}
int main() {test01();system("pause");return EXIT_SUCCESS;
}

总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
class Base {
public:static void func(){cout << "Base - static void func()" << endl;}static void func(int a){cout << "Base - static void func(int a)" << endl;}static int m_A;
};int Base::m_A = 100;class Son : public Base {
public:static void func(){cout << "Son - static void func()" << endl;}static int m_A;
};int Son::m_A = 200;//同名成员属性
void test01()
{//通过对象访问cout << "通过对象访问: " << endl;Son s;cout << "Son  下 m_A = " << s.m_A << endl;cout << "Base 下 m_A = " << s.Base::m_A << endl;//通过类名访问cout << "通过类名访问: " << endl;cout << "Son  下 m_A = " << Son::m_A << endl;cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}//同名成员函数
void test02()
{//通过对象访问cout << "通过对象访问: " << endl;Son s;s.func();s.Base::func();cout << "通过类名访问: " << endl;Son::func();Son::Base::func();//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问Son::Base::func(100);
}
int main() {//test01();test02();system("pause");return 0;
}

总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通过类名)

多继承语法

C++允许一个类继承多个类

语法:class 子类 :继承方式 父类1 , 继承方式 父类2...

多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议用多继承

class Base1 {
public:Base1(){m_A = 100;}
public:int m_A;
};class Base2 {
public:Base2(){m_A = 200;  //开始是m_B 不会出问题,但是改为mA就会出现不明确}
public:int m_A;
};//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1
{
public:Son(){m_C = 300;m_D = 400;}
public:int m_C;int m_D;
};//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{Son s;cout << "sizeof Son = " << sizeof(s) << endl;cout << s.Base1::m_A << endl;cout << s.Base2::m_A << endl;
}int main() {test01();system("pause");return 0;
}

总结: 多继承中如果父类中出现了同名情况,子类使用时候要加作用域

菱形继承

菱形继承概念:

两个派生类继承同一个基类

又有某个类同时继承者两个派生类

这种继承被称为菱形继承,或者钻石继承

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YbOVXJHJ-1617107726866)(C:\Users\hester\Desktop\捕获.PNG)]

B和C从A中继承,而D多重继承于B,C。那就意味着D中会有A中的两个拷贝。因为成员函数不体现在类的内存大小上,所以实际上可以看到的情况是D的内存分布中含有2组A的成员变量

class Animal
{
public:int m_Age;
};//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo   : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};void test01()
{SheepTuo st;st.Sheep::m_Age = 100;st.Tuo::m_Age = 200;cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;cout << "st.m_Age = " << st.m_Age << endl;
}int main() {test01();system("pause");return 0;
}

总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  • 利用虚继承可以解决菱形继承问题

多态


洗牌算法:

分析洗牌算法正确性的准则:产生的结果必须有 n! 种可能,否则就是错误的。**这个很好解释,因为一个长度为 n 的数组的全排列就有 n! 种,也就是说打乱结果总共有 n! 种。算法必须能够反映这个事实,才是正确的。

// 得到一个在闭区间 [min, max] 内的随机整数
int randInt(int min, int max);// 第一种写法
void shuffle(int[] arr) {int n = arr.length();/******** 区别只有这两行 ********/for (int i = 0 ; i < n; i++) {// 从 i 到最后随机选一个元素int rand = randInt(i, n - 1);/*************************/swap(arr[i], arr[rand]);}
}// 第二种写法for (int i = 0 ; i < n - 1; i++)int rand = randInt(i, n - 1);// 第三种写法for (int i = n - 1 ; i >= 0; i--)int rand = randInt(0, i);// 第四种写法for (int i = n - 1 ; i > 0; i--)int rand = randInt(0, i);

构造函数可以是虚函数吗,为什么

不可以

  1. 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。。。

  2. 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初始化,将无法进行。

new和malloc的区别

  • 属性:new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持
  • 参数:使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸
  • 返回类型:new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
  • 分配失败:new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
  • 自定义类型: new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
  • 重载:C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。
  • 内存区域:new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。

delete和delete[]

基本的数据类型对象没有析构函数,但类不一样。假设通过new申请了一个对象数组,注意是对象数组,返回一个指针,对于此对象数组的内存释放,需要做两件事情:一是释放最初申请的那部分空间,二是调用析构函数完成清理工作。对于内存空间的清理,由于申请时记录了其大小,因此无论使用delete还是delete[ ]都能将这片空间完整释放,而问题就出在析构函数的调用上,当使用delete时,仅仅调用了对象数组中第一个对象的析构函数,而使用delete [ ]的话,将会逐个调用析构函数

c++中四种类型cast(强制)转换

1、const_cast

用于将const变量转为非const。
用来修饰类型的const或volatile属性。除了去掉const或者volatile修饰之外,type_id和expression得到的类型是一样的。
但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。
2、static_cast

用于各种隐式转换,比如非const转const,void * 转指针等, static_cast能用于多态向上转化,如果向下转能成功但是不安全,结果未知;
用于基本数据类型之类的转换,如把int转换为char,把int转换成enum。这种转换的安全性也要开发人员来保证。
3、dynamic_cast

用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,如果是非法的对于指针返回NULL,对于引用抛异常。 要深入了解内部转换的原理。

向上转换:指的是子类向基类的转换。
向下转换:指的是基类向子类的转换。
它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。

dynamic_cast会动用运行时信息RTTI来进行类型安全检查,因此dynamic_cast存在一定的效率损失。当使用dynamic_cast时,该类型必须含有虚函数,这是因为dynamic_cast使用了存储在vtable中的信息来判断实际的类型,RTTI运行时类型识别用于判断类型。typeid表达式的形式时typeid(e),typeid操作的结果是一个常量对象的引用,该对象的类型是type_info或type_info的派生。
4、reinterpret_cast

几乎什么都可以转,比如将 int转指针 ,可能会出问题,尽量少用;
可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。

为什么不使用C的强制转换?

C的强制转换表面上看起来功能强大什么都能转,但是转化不够明确,不能进行错误检查,容易出错。

C++基础面试(持续更新~~~)相关推荐

  1. java基础(适合零基础)持续更新

    目录 java语言的概述 1.概述 2.Java语言特点 3.java的加载与执行 开始第一个java程序 1.安装JDK(这里安装JDK1.8) 2.安装文本编辑器Editplus 3.JDK目录介 ...

  2. 幻想-FLEX 3基础视频教程 持续更新中

    欢迎点击此处订阅本Blog title="RSS 2.0" type="application/rss+xml" href="http://feed. ...

  3. 2021面试前期准备——JS基础(持续更新)

    JS基础(一) (1).数据类型 1.js数据类型: 8种 js基础类型:5种 js引用类型:3种 2.null.undefined.isNaN,NaN null是一个表示"无"的 ...

  4. JavaScipt基础(持续更新)

    JavaScipt基础 JavaScipt基础 一.JavaScipt介绍 1.1JavaScript编程语言 1.2发展历史 1.3JavaScript和HTML.CSS 1.4JavaScript ...

  5. js基础笔记(持续更新)

    为什么有的编程规范要求用 void 0 代替 undefined? 因为 JavaScript 的代码 undefined 是一个变量,而并非是一个关键字,这是 JavaScript 语言公认的设计失 ...

  6. JAVA基础(持续更新中)

    JAVA基础 2020年11月27日 21:01 1 预科 a. 什么是计算机 能够按照程序运行,自动.高速处理海量数据的现代化智能电子设备. 由硬件常见的形式有台式计算机.笔记本计算机.大型计算机等 ...

  7. Node.js零基础自学(持续更新中)

    1. Node.js时基于Chrome V8 引擎的JavaScript运行环境.官网:Node.jsNode.js® is a JavaScript runtime built on Chrome' ...

  8. scrapy基础(持续更新)

    环境: Python3.7 scrapy2.4 官方文档(英文)scrapy2.4:https://docs.scrapy.org/en/latest/intro/overview.html 官方文档 ...

  9. #AMBER 分子动力学软件Amber18介绍与基础教程(持续更新)

    Amber是由多模块所组成的分子动力学软件,其工作流主要如下: 其中又分为: 1.系统预处理预处理(pdb预处理,LEAP,antechamber和gaff等) 2.运行动力学模拟(sander,pm ...

  10. 嵌入式硬件开发基础(持续更新)

    电阻 理论基础 电阻的定义 电荷在导体中运动时,形成电流.导体中的分子.原子等其他粒子阻碍电荷移动,进而阻碍电流这种阻碍作用,我们称之为电阻. 欧姆定律 经典公式:I=U/RI=U/RI=U/R 交换 ...

最新文章

  1. web框架总结(django、flask)
  2. 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---20
  3. 用微信小程序开发的Canvas绘制可配置的转盘抽奖
  4. #翻译# 关于 Java 性能方面的 9 个谬论
  5. IAR6.1的工程迁移到IAR6.5不能用的解决方法
  6. centos下python中添加easygui模块
  7. 注解返回html页面,【提问】ajax请求返回整个html页面
  8. 获取synchronized锁中的阻塞队列中的线程是非公平的
  9. 利用SQL对拍拍贷数据进行逾期分析
  10. 泛泰A870 CWM Recovery En/Cn合集
  11. 灵雀云 CTO 陈恺:从“鸿沟理论”看云原生,哪些技术能够跨越鸿沟?
  12. 如何评价柏拉图_哲学家如何看待死亡?|读柏拉图《斐多篇》(1)
  13. VUE中的鼠标右键功能
  14. linux uwsgi 非root,只能以root身份运行uwsgi
  15. 华为实验-关于不同vlan之间的互通 混合实验
  16. android qq侧滑,Android实现QQ的侧滑置顶、删除
  17. 对《java程序员上班那点事》笔者对数组占用内存质疑
  18. 4年,如何从草根成长成为CTO-(第一篇)
  19. 【CVPR2021】【语义编辑】SeFa(Closed-Form Factorization of Latent Semantics in GANs)论文分析
  20. 【机器学习中的数学】函数空间

热门文章

  1. 计算机网络谢希仁第七版课后习题答案(第六章)
  2. openstack创建实例报Build of instance d401db9e-xxxx-97c5d7685592 aborted: Unknown auth type: None
  3. CET-4 week8# 细枝末节
  4. Java---正则表达式
  5. 3D视觉(二):单目摄像头的标定与校正
  6. nexus运行时异常org.apache.http.conn.ConnectTimeoutException
  7. jzoj5984. 【北大2019冬令营模拟2019.1.1】仙人掌 (分块)
  8. 图像处理方面的sci期刊_图像处理领域的SCI期刊.doc
  9. jQuery获取和计算对象(object)的长度
  10. wd移动硬盘插电脑没反应_西部数据移动硬盘插电脑上不显示?