c++基础04

Posted AI视觉与编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++基础04相关的知识,希望对你有一定的参考价值。

day03(仅剩233天)

When you want to give up, think about what made you insist on coming here.--kobe bryant

多态

day02

2021.1.07 c++知识点

多态 多态分为两类,静态多态和动态多态 静态多态:函数重载,运算符重载,复用函数名 动态多态:派生类和虚函数实现运行时多态

两者的区别:

#include<iostream>
using namespace std;

class Animal
{
public:
virtual void speak()
{
cout << "动物在说话" << endl;
}
};

class Cat :public Animal
{
public:
void speak() //重写父类虚函数
{
cout << "小猫在说话" << endl;
}
};

class Dog :public Animal
{
public:
void speak() //重写父类虚函数
{
cout << "小狗在说话" << endl;
}
};

void Dospeak(Animal &animal)
{
animal.speak();
}

void test01()
{
Cat cat; //创建对象
Dospeak(cat);//函数调用

Dog dog; //创建对象
Dospeak(dog); //函数调用
}

int main()
{
test01();
system("pause");

return 0;
}

多态满足的条件:

1、有继承关系

2、子类重写父类中的虚函数

多态使用条件:

父类指针或者引用指向子类对象(例如上面的父类的引用animal指向dog或者cat)

重写:函数返回值类型,函数名、参数列表完全一致称为重写

//多态案例--实现一个简单的计算器
#include<iostream>
using namespace std;

class AbstractCalculator
{
public:
int num1;
int num2;
virtual int getResult() //父类的虚函数
{
return 0;
}
};

//构建加法计算器
class Add :public AbstractCalculator //继承
{
public:
int getResult() //重写父类虚函数
{
return num1 + num2;
}
};

//减法计算器
class Sub :public AbstractCalculator //继承
{
public:
int getResult() //重写父类虚函数
{
return num1 - num2;
}
};

//构建乘法计算器
class Mul :public AbstractCalculator
{
public:
int getResult()
{
return num1 * num2;
}
};

//构建除法计算器
class Del :public AbstractCalculator
{
public:
int getResult()
{
return num1 / num2;
}
};


//测试
void test01()
{
//创建加法计算
AbstractCalculator* abc = new Add; //在堆区创建
abc->num1 = 10;
abc->num2 = 10;
cout << abc->num1 << " + " << abc->num2 << " = " << abc->getResult() << endl;
delete abc; //用完记得销毁

//创建减法计算器
abc = new Sub;
abc->num1 = 200;
abc->num2 = 50;
cout << abc->num1 << " - " << abc->num2 << " = " << abc->getResult() << endl;
delete abc; //用完记得销毁
}

int main()
{
test01(); //函数调用
system("pause");

return 0;
}

纯虚函数和抽象类

在多态中,通常父类中虚函数的实现都是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改写为纯虚函数

语法:virtual 返回值类型 函数名(参数列表)=0

当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:

1、无法实例化对象

2、子类必须重写抽象类中的纯虚函数,否则也属于抽象类

//纯虚函数
#include<iostream>
using namespace std;

class Base
{
public:
virtual void func() = 0; //纯虚函数
};

class Son :public Base
{
public:
virtual void func() //重写父类中的纯虚函数
{
cout << "func()调用" << endl;
}
};

void test01()
{
Base* base = NULL;
//base = new Base; 错误,纯虚函数无法实例化对象
base = new Son; //子类可以实例化对象
base->func();
delete base; //销毁对象
}

int main()
{
test01();
system("pause");

return 0;
}

虚析构和纯虚析构

多态在使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方式:将父类中的析构函数改为纯虚析构或者虚析构

虚析构与纯虚析构的共性:

1、可以解决父类指针释放子类对象

2、都需要具体的实现

区别:

如果是纯虚析构,该类属于抽象类,无法实例化对象

/虚析构和纯虚析构
#include<iostream>
using namespace std;

class Animal
{
public:
Animal()
{
cout << "Animal的构造函数" << endl;
}
virtual void speak() = 0; //纯虚函数

//~Animal() //如果不改写为纯虚析构,子类的析构函数将不会执行
//{
// cout << "Animal的析构函数调用" << endl;
//}

/*virtual ~Animal()
{
cout << "Animal的虚析构函数调用" << endl;
}*/

virtual ~Animal() = 0; //纯虚析构函数,这里只是声明,需要具体实现
};

Animal::~Animal()
{
cout << "Animal的纯虚析构函数调用" << endl;
}


class Cat :public Animal
{
public:
string* m_name; //指针
Cat(string name)
{
cout << "Cat构造函数调用" << endl;
m_name = new string(name); //子类对象在堆区创建数据
}

virtual void speak() //子类重写父类虚函数
{
cout << *m_name << "小猫在说话" << endl;
}

~Cat()
{
cout << "Cat析构函数调用" << endl;
if (this->m_name != NULL)
{
delete m_name;
m_name = NULL;
}
}
};

void test01()
{
Animal* animal = new Cat("Tom");
animal->speak();
delete animal;
}

int main()
{
test01();
system("pause");

return 0;
}

模板

普通函数与函数模板的区别:

1、普通函数调用时可以发生自动类型转换(隐式转换)

2、函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

3、如果利用显示指定类型的方式,可以发生隐式类型转换

//模板
#include<iostream>
using namespace std;

int myAdd01(int a, int b) //普通函数
{
return a + b;
}

//模板
template<class T>
T myadd02(T a, T b)
{
return a + b;
}

void test01()
{
int a = 20;
int b = 30;
char c = 'c';

cout << myAdd01(a, c) << endl; //正确,可以将c转换为99
//cout << myadd02(a, c) << endl; //错误,不会发生隐式类型转换
cout << myadd02<int>(a, c) << endl; //正确,可以发生隐式类型转换
}

int main()
{
test01();
system("pause");

return 0;
}

函数模板与普通函数调用规则:

1、如果函数模板和普通函数都能实现,优先调用普通函数

2、可以通过空模板参数列表实现函数模板调用(即函数<>(参数列表)

3、函数模板也可以发生重载

4、如果函数模板可以产生更好的匹配,优先调用函数模板

类模板和函数模板的区别:

类模板没有自动推导类型

类模板在模板参数中可以有默认参数

类模板中的成员函数与普通成员函数的区别:

类模板中的成员函数在调用时创建,普通的成员函数一开始就创建

类模板与继承:

当子类继承的父类是一个类模板时,子类在声明的时候,要指定父类中T的类型,如果不指定,编译器无法给子类分配内存

如果想灵活指定出父类中T的类型,子类也需要变为类模板

//类模板与继承
#include<iostream>
using namespace std;

template<class T>
class Base
{
T m;
};

//class Son :public Base {}; 错误,因为没有指出父类中T的类型
class Son :public Base<int>
{};



int main()
{
system("pause");

return 0;
}



c++ prime01

字符和字符串字面值

字符串字面值的实际类型类型是由常量字符构成的数组

编译器在每个字符串的末尾添加一个空字符(‘\0')

char c = 'a';  //只有一个字符a
char ca = "hello"; //表示hello+\0

变量

变量的初始化和赋值不是一样,初始化不是赋值,初始化的含义是创建变量的同时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新的值来代替。

int a =10;  //初始化
a = 30; //赋值

默认初始化

如果变量定义时没有指定初值,变量被默认初始化,默认值是多少由变量的类型决定。

变量声明和定义的关系:

声明规定了变量的类型和名字,定义除了上述操作外,还为变量申请了存储空间,也可能会为变量赋一个初值。

如果想声明而非定义,可以用extern关键字

extern int i;  //声明
int j; //声明并定义
extern double pi = 3.1416; //定义

变量只能被定义一次,但是可以被声明多次

引用只能绑定在对象上,不能与字面值或者某个表达式的计算结果绑定,必须初始化

int i=10;
int &b = i;//引用,起别名
int &c; //错误,必须初始化
int &d=10; //错误,不能和字面值绑定

void*指针

指向指针的指针

//指向指针的指针
#include<iostream>
using namespace std;

int main()
{
int a = 10;
int* p = &a; //P指向a,存的是a的地址
int** q = &p; //q指向p,存的是q的地址,是指向指针的指针

//输出a的三种方法
cout << "a=" << a << endl; //10
cout << "a = " << *p << endl; //10
cout << "a= " << **q << endl; //10

system("pause");
return 0;
}

指向指针的引用

引用本身不是一个对象,因此不能定义指向引用的指针,但是指针是对象,所以存在对指针的引用

//指向指针的引用
#include<iostream>
using namespace std;

int main()
{
int a = 10;
int b = 20;
int* p = &a; //定义了一个指针指向变量a,p存放的是a的地址

int*& r = p; //指向指针的引用,相当于给指针p起了一个别名r,从右往左看,&离r最近,确定r是一个引用,在看其他部分,表示是一个指向指针的引用
r = &b; //将r指向b,相当于将p指向b

//输出此时p所指变量的值
cout << "p所致变量的值为:" << *p << endl; //20

system("pause");

return 0;
}

如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字

constexpr变量

一般来说,如果认定某个变量是一个常量表达式,就可以声明为constexpr

constexpr int mf=20;  //20是常量表达式

auto类型说明符

让编译器替我们分析表达式所属的类型

auto item = val1 + val2;  //item 初始化为val1 + val2的结果

auto声明的类型必须保持一致

auto i=0,*p=&i;  //正确,因为i是整型,p是指向整型的指针
auto sz=0,pi=3.14; //错误,sz和pi的类型不一样

字符串、向量和数组

vector对象(以及string对象)的下标运算符可用于访问已经存在的元素,而不能用于添加元素,只能用push_back;

迭代器

类似于指针类型,提供了对对象的间接访问,就迭代器而言,其对象是容器中的元素或者string对象中的字符

如果容器为空,begin和end返回的是同一个迭代器,都是尾后迭代器


以上是关于c++基础04的主要内容,如果未能解决你的问题,请参考以下文章

有趣的 C++ 代码片段,有啥解释吗? [复制]

以下代码片段 C++ 的说明

C++ 代码片段执行

此 Canon SDK C++ 代码片段的等效 C# 代码是啥?

C++ 代码片段(积累)

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情