一,C++对C一些扩展的学习
1,namespace使用和注意点
namespace的定义的
1,namepspace命名空间的里面变量的作用域声明 必须是全局变量
namespace LOL
{
const int a = 90; //声明成全局变量
void test();
}
2, 命名空间存在的意义是避免二义性
解释:就是变量的在不同的命名空间就前面加上命名空间, 在C语言中这样的定义使用会重名
3,命名空间可以有变量,函数,结构体,类
4,命名空间可以圈套命名空间
namespace LOL1
{
int a = 90;
namespace chen
{
int b = 80;
}
}
//命名空间取别名
namespace che = LOL1;
5,命名空间可以后期扩展, 同名的不会覆盖 而是合并
6,命名空间可以匿名
7,命名空间可以起别名
2,using声明以及using编译指令
using 声明碰到函数重载
namespace A{
void func(){}
void func(int x){}
int func(int x,int y){}
}
void test(){
using A::func;
func();
func(10);
func(10, 20);
}
3,C++ 对C的增强
1,全局变量检测的增强
下面代码在C中可以C++不可以
int a;
int a = 80;
2.函数检测
1.参数类型检测
//函数在C语言中不用写明函数参数的类型而在C++中必须写类型
int myswap(a, b);
2.返回值的检测
在C语言中可以不用写函数的返回值编译器会waring ,C++必须要有返回值
int myswap(a, b)
{
};
3,函数调用 传参个数
在C语言中可以传入多个参数,在C++中可以传多个参数
int myswap(int a, int b);
int main(void)
{
myswap(89, 90, 89, 89); //在C语言中可以传入多个参数
return 0;
}
3,类型转换增强
C++中malloc后必须转换为 需要的类型
4,struct增强
1,C++中可以放函数
2,C++中使用结构体, 可以省略关键字
3,bool类型
sizeof bool = 1
4,三目运算符增强
C++中返回 变量 可以继续作为左值运算
int a = 10;
int b = 20;
printf("ret:%d\n", a > b ? a : b);
//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?
cout << "b:" << b << endl;
//返回的是左值,变量的引用
(a > b ? a : b) = 100;//返回的是左值,变量的引用
cout << "b:" << b << endl;
三目运算的本质是:
*(a > b ? &a : &b ) = 100;
5,const增强
1,C和C++对const处理的方式
C语言中 局部的const变量 会分配内存, 可以通过指针间接修改,为常量,不许初始化数组
C++中局部的const 会放到符合表中,不可以通过指针修改到本体,可以作为为常量 初始化数组
const在C语言中默认是外部链接,可以在其他文件中进行访问
const在C++中 默认是内部链接,需要利用extern提供作用域,改成外部链接
4,引用的基本语法
引用实际是变量的别名吧了
int u = 90;
int const *p = u;
1,语法
语法:类型 &别名 = 原名
2,引用必须要初始化 一旦初始化后就不可以修改指向
int a1 = 90;
int &b1 = a1;
int &b1 = a; //err
3,对数组建立引用的操作
int arr[] = { 4, 45, 454, 34, 9 };
//方法一:
int(&aprr)[5] = arr;
int i;
for (i = 0; i < 5; i++)
cout << aprr[i] << endl;
//方法二typedef
typedef int parr[5];
parr& p = arr;
for (i = 0; i < 5; i++)
cout << p[i] << endl;
4,函数参数的是引用
//引用的使用
void set_name(char* &name) //实际还是地址引用
{
n_name = name;
}
二,函数重载和类的封装
1,C++函数的默认参数
1,语法:
语法: 类型 变量名称 = 默认值
void load(int a = 90000, int b = 100) ///默认值
{
cout << "a = " << a << "b = " << b << endl;
}
int main(void)
{
load(3, 9); //
system("pause");
return 0;
}
默认参数会被转过去的值输替代
运行效果图
2,参数中有一个参数有了默认值, 那该位置开始从左到右都必须有默认值
例子:
void load(int a, int b = 90, int c = 1000, int d = 440)
{
cout << "a = " << a << "b = " << b << "c = " << c
<< "d = " << d << endl;
}
int main(void)
{
load(3, 4, 5, 6); //参数
system("pause");
return 0;
}
3,当函数的声明和实现中, 只能有一个出现默认参数
void load(int a, int b);
void load(int a, int b, int c, int d);
2,占位符参数
占位符不能使用在头文件中, 占位符是默认值相当于没有
例子:
//占位符
void placeholder2(int a, int = 90) //占位符 int = 90
{
cout << "a = " << a << endl;
}
int main(void)
{
//占位符
placeholder2(9000);
system("pause");
return 0;
}
3,函数重载
- 在同一作用域下
- 函数的参数不同, 或者类型不同, 或者顺序不同
- 函数的返回值不能作为重载的条件
1,重载
//函数的重载方法名相同参数不同
void overload()
{
cout << "overload()" << endl;
}
void overload(int a)
{
cout << "overload(int a)" << endl;
}
void overload(int a, int b)
{
cout << "overload(int a, int b)" << endl;
}
2,引用重载
例子1.
void referen(int& a) //int const * a //引用是一块内存的空间 //没有内存空间是不可以的
{
cout << "a = " << a << endl;
}
void test()
{
int a = 4;
referen(a);
}
例子2.
void referen2(const int &a) //const int cont * a
{
cout << "a = " << a << endl;
}
void test03()
{
referen2(8);
}
3,函数重载
函数重载碰到默认参数:注意避免二义性
例子:
void overload()
{
cout << "overload()" << endl;
}
void overload(int a)
{
cout << "overload(int a)" << endl;
}
void overload(int a, int b = 9090)
{
cout << "overload(int a, int b = 9090)" << endl;
}
/*
void overload(int a, int b)
{
cout << "overload(int a, int b)" << endl;
}*/
int main(void)
{
//overload(4); //err他不知道选择那个一个
overload(); //ok
overload(5, 9); ok
system("pause");
return 0;
}
4,extern "C"的使用
方法1:
extern "C" void show(); //声明C函数
方法2:
#ifndef __cpluscplus
extern "C" {
#endif // !__cpluscplus
}
#ifndef __cplusplus
#endif // !__cplusplus
5,函数封装
- 将属性和行为作为一个整体了表现在生活的数据
- 加权限 public, protected, private
- C++中struct与class的区别是权限的不同, struct结构体默认是public的,class默认是private属性
6,C的宏定义和C++加强内联函数inline的区别
#define SUM(a , b) (a + b);
#define MAX(a, b) (a > b ? a : b)
int main(void)
{
int a = 90, b = 3;
SUM(++a, b); // (++a + b);
MAX(++a, b); //(++a > b ? ++a : b) //区别
return 0;
}
使用内联函数inline就不会出现++a的现象
#define SUM(a , b) (a + b);
#define MAX(a, b) (a > b ? a : b)
int main(void)
{
int a = 90, b = 3;
SUM(++a, b); // (++a + b);
MAX(++a, b); //(++a > b ? ++a : b) // 区别
return 0;
}
使用内联函数inline就不会出现++a的现象
三,类的深拷贝浅拷贝问题 explicit(隐式法 )的使用
1,类的构造函数和析构函数的虚函数的表示
- 括号法 (类名 别名(参数))
- 显示法 (类名 别名 = 类名(参数))
3. 隐式法 (类名 别名 = 参数)
3. 赋值的是的const使用 void* getname() const;
2,拷贝函数的使用场景
4. 创建一个类的时候使用拷贝函数
5. 值传递方式会调用拷贝函数
6. 以返回值类 方式调用拷贝函数
#include "../include/Game.h"
Game::Game()
{
cout << "Game 无参构造函数" << endl;
}
void Game::setG(char* name)
{
g_name = name;
}
char* Game::getG() const
{
return g_name;
}
Game::Game(const Game& g)
{
cout << "Game 拷贝构造函数" << endl;
g_name = (char*)malloc(sizeof(g.getG()) + 1);
memset(g_name, 0, sizeof(g.getG()) + 1);
strcpy(g_name, g.getG());
}
Game::~Game()
{
cout << "Game 析构函数" << endl;
// 释放内存
/*if (g_name != NULL)
free(g_name);
g_name = NULL;*/
}
Phone
#include "../include/Phone.h"
void Phone::setp(char* name)
{
p_name = name;
}
char* Phone::getP()const
{
return p_name;
}
Phone::Phone()
{
cout << "Phone 无参数构造函数" << endl;
}
Phone::Phone(const Phone& p)
{
cout << "Phone 拷贝构造函数" << endl;
p_name = (char*)malloc(sizeof(p.getP()) + 1);
memset(p_name, 0, sizeof(p.getP()) + 1);
strcpy(p_name, p.getP());
}
Phone::~Phone()
{
cout << "Phone 析构函数" << endl;
/*if (p_name != NULL)
free(p_name);
p_name = NULL;*/
}
Person
#include "../include/Person.h"
void Person::setName(char* name)
{
m_name = name;
}
char* Person::getName() const
{
return m_name;
}
void Person::setPhone(Phone& p)
{
m_phone = p;
}
Phone Person::getPhone() const
{
return m_phone;
}
void Person::setGame(Game& g)
{
m_game = g;
}
Game Person::getGame() const
{
return m_game;
}
Person::Person()
{
cout << "Person 无参构造函数" << endl;
}
Person::Person(const Person& p)
{
cout << "Person 拷贝构造函数" << endl;
m_name = (char*)malloc(sizeof(p.getName()) + 1);
strcpy(m_name, p.getName());
m_phone = p.getPhone();
m_game = p.getGame();
}
void Person::isplay()
{
cout << m_name << m_phone.getP() << m_game.getG() << endl;
}
Person::~Person()
{
cout << "Person 析构函数" << endl;
/*if (m_name != NULL)
free(m_name);
m_name = NULL;*/
}
test
#include "../include/Phone.h"
#include "../include/Game.h"
#include "../include/Person.h"
void test()
{
Person p;
p.setName("chen");
Phone p2;
p2.setp(" 小米");
Game g;
g.setG(" 王者荣耀");
p.setPhone(p2);
p.setGame(g);
p.isplay();
}
int main(void)
{
test();
system("pause");
return 0;
}
效果图
深拷贝构造函数使用的时自己要添加 , 不是可以不用写了,编译器已经把写好了
3.explicit隐式法的使用 ,Google推荐使用隐式法的
Google禁止使用隐式法
class Person
{
private:
int m_a;
public:
Person(int a){ this->m_a = a};
};
void test01()
{
Person p = 1; //google 禁止使用隐式法的这样后期维护难
}
Google推荐explicit
class Person
{
private:
int m_a;
public:
explicit Person(int a){ this->m_a = a};
};
void test01()
{
//Person p = 1; //err
Person p(1);
}
//Google 推荐的
4.new, delete
①, malloc 计算分配多少内存要判断是否分配成功
② malloc 不会调用构造函数 而 new会调用构造函数
③ free 不会调用析构函数 delete 会调用析构函数
④ malloc 和 free 配套使用 ,他们都属于库函数
⑤ new 和 delete 配套使用 ,他们属于运算符
⑥ malloc 返回的是 void* 需要强制类型转换 ,而 new返回是该对象的指针,不需要类型转
换
⑦不要用 void* 接受 new出来的对象,原因是无法进行释放
⑧利用 new创建数组 delete 时候 要加【】 delete [] 数组名
四,引用作为返回值的
- 返回引用的函数实际上是被引用的变量的别名
- 在作为函数参数和函数的返回值的引用
free_throws & accmulate(free_throws & target,
const free_throws & source)
{
target.attempts += source.attempts;
target.made += source.made;
set_pc(target);
return target;
}
函数accmulate使用有好几种
- accmulate(team, one); display(team);
- display(accmulate(team, two));
- accmulate(accmulate(team, three), four);
- dup = accmulate(team, five);
- accmulate(dup, five) = team;
2,将 const用于引用返回类型
const free_throws & accmulate(free_throws & target,
const free_throws & source);
那么函数的一些的调用就不可以使用了
- accmulate(accmulate(team, three), four);
- accmulate(dup, five) = team;
3,将引用用于类对象
string version1(const string & s1,
const string & s2)
{
string temp;
temp = s2 + s1 + s2;
return temp;
}
const string & version2(string & s1,
const string & s2)
{
s1 = s2 + s1 + s2;
return s1;
}
const string & version3(string & s1,
const string & s2)
{
string temp;
temp = s2 + s1 + s2;
return temp;
/*s1 = s2 + s1 + s2;
return s1;*/
}
测试test
void test01()
{
string input;
string copy;
string result;
cout << "Enter a string: ";
getline(cin, input);
copy = input;
cout << "Your string as entered: " << input << endl;
result = version1(input, "***");
cout << "Your string enhanced: " << result << endl;
cout << "Your string enhanced: " << result << endl;
cout << "Your original-string:" << input << endl;
result = version2(input, "###");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;
cout << "Resetting original string: \n";
input = copy;
result = version3(input, "@@@"); //err
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;
}
4,何时使用引用参数
使用引用参数的主要原因有两个
-
程序员能够修改调用函数中的数据对象。
-
通过传递引用而不是整个数据对象,可以提高程序的运行速度。
当数据对象较大时(如结构和类对象),第二个原因最重要。这些也是使用指针参数的原因。
这是有道理的,因为引用参数实际上是基于指针的代码的另一个接口。那么,什么时候应
使用引用,什么时候使用指针呢?什么时候应按值传递呢?下面是一些指导原则:
一,对于使用传递的值而不作修改的函数 -
如果数据对象很小,如内置数据类型或小型结构,可以按值传递。
-
如果数据对象是数组,值使用指针,因为这是唯一的选择,并将指针声明为指向const的指
针。 -
如果数据对象是较大的结构,使用const指针或者const引用,以提高程序的效率。这样可以
节省复制结构所需的时间和空间。 -
如果数据对象是类对象,使用const引用。类设计的语义常常要 求使用引用,这是C++新增
这项的主要原因。因此,传递类对象参数的标准分数是按引用传递。
二,对于修改调用函数中数据的函数 -
如果数据对象是内置数据类型,则使用指针。如果看到诸如fixit(&x)这样的代码(其中x是
int),则很明显,该函数将修改x。
2. 如果数据对象是数组,则只能使用指针。
3. 如果数据对象是结构,则使用引用或指针。
4. 如果数据对象是类对象,则使用引用。
五,静态,常量,单例模式, this指针和有元
1,静态和常量的关系总结
①静态常量和静态函数的关系
- 静态常量必须在类的外面赋值
- 静态常量在程序编译阶段就已经赋值了
- 静态常量是静态函数来管理的和改变只,类中函数改变只是静态常量一个临时的值
- 静态函数是全局的可以访问的
②常量和常量函数的关系
- 常量在格式是const int 数据类型;
- 常量不可以是不可以修改的,但是要修改可以在声明的时候加上
mutable关键字 是可
修改的
- 常函数的格式是 返回值 方法名(参数) const; 的格式
- 常函数不可以修改内容的只可以读的工作的有一个例外加上 mutable的变量
- 常类只可以访问常函数的因为常类可以修改内容的
③静态和常量总结
2,单例模式
说明:单例模式是只有一个实例,所有的其他的都是用同一实例就叫单例模式,而且这个实例当
程序运行起来时存在直到程序结束
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
// 使用标准的命名空间
class Printer
{
private:
int sum;
static Printer* singlePrinter;
Printer()
{
cout << " 无参构造函数" << endl;
sum = 0;
}
// 拷贝函数的
Printer(const Printer& p)
{
cout << " 拷贝构造函数" << endl;
}
public:
// 得到实例
static Printer* getInstanect()
{
return singlePrinter;
}
void getShowTest(string name)
{
cout << name << endl;
sum = 1 + sum;
cout << sum << endl;
}
~Printer()
{
cout << " 析构函数" << endl;
}
};
// 实例化
Printer* Printer::singlePrinter = new Printer;
void test01()
{
Printer* p1 = Printer::getInstanect();
p1->getShowTest(" 陈丽");
p1->getShowTest(" 王蓉");
Printer* p2 = Printer::getInstanect();
p2->getShowTest(" 王盼盼");
if (p1 == p2)
cout << " 相同" << endl;
else
cout << " 不相同" << endl;
}
int main(void)
{
test01();
system("pause");
return 0;
}
3,*this指针的使用
Person& getAdd(int age)
{
this->setAge(this->m_age + age);
return *this;
}
4,有元函数和有元类
声明的格式分别是
- 有元函数: friend 返回类型 方法名 (参数);
- 有元类: friend class 类名;
- 有元类中有元函数:friend 作用域::方法名(参数);
class Person
{
friend void test();
private:
int m_a;
char* che;
};
void test()
{
Person p;
p.m_a = 90;// 可以访问,赋值操作
cout << p.m_a << endl;
}
六,数组类的封装,运算符重载和智能 this指针的使用
1,数组类的封装
UML的结构图
文件的声明:
class MyArray
{
public:
/* 数组类的拷贝构造函数 */
MyArray(const MyArray&arr);
/* 数组类的有参构造函数参构造函数 */
MyArray(int capactity);
/* 数组类的析构函数 */
~MyArray();
/* 数组类的无参构造函数 */
void Myarray(void);
/* 在数组的尾巴插入数据val */
void Tail_pash(int val);
/* 按照位置index 位置改变数组的index 位置的值 */
void set_arr_val(int index, int val);
/* 获取数组index 值 */
int getValData(int index);
/* [] 运算符重载 index */
Myarray& operator[](int index);
/* 获取数组类的长度 */
int getLength(void);
protected:
private:
/* 数组的已经有的数据大小size */
int m_Size;
/* 数组类的首地址 */
int* m_Address;
/* 数组类的容量大 */
int m_Capatity;
};
头
2,运算符重载
① ++,–运算符重载
我发现一个问题就是引用作为返回值时在 gcc上是报错的
前置++,–的格式
对象名& operator++()
{
this-> 变量++ ;
return *this;
}
后置++,--的格式是要找到一个中间变量
对象名 operator++(int)
{
对象名 temp;
temp. 变量 = this-> 变量;
this-> 变量++;
return temp;
}
② <<,>>运算符操作
一般使用全局函数作为<<,>> 要声明成有元函数
格式:friend ostream& operator<<(ostream& os, 对象名& p);
操作:
ostream& operator<<(ostream& os, 对象名& p)
{
os << p. 变量 ;
return os;
}
3,智能指针 this的使用
UML图
头文件声明: pointer
#include <Pointer.h>
class SmartPointer
{
public:
/* SmartPointer 析构函数释放Pointer 的堆上内存 */
~SmartPointer();
/* SmartPointer 构造函数 */
SmartPointer(Pointer* p);
/* -> 指针的重载 */
Pointer* operator->(void);
/* * 管理Pointer 指针函数 */
Pointer& operator*(void);
protected:
private:
Pointer* poiner;
};
重点:拷贝构造函数只在初始化的时候会调用
七 .关系运算符的封装 MyString和继承的静态( static),构造和析构,
虚继承 virtual偏移量
1,关系运算符重载 =,>>,<<
/* << 运算符重载打印
friend ostream&
/* >> 赋值的操作的
friend istream&
*/
operator<<(const MyString& my_str);
*/
operator>>(const MyString& my_str);
总结: &&, ||这样的运算符是可以重载的但是重载后就没有本身的一些性质
2,MyString类的封装
UML结构体
头文件的声明:
#if !defined(__MyString_MyString_h)
#define __MyString_MyString_h
class MyString
{
public:
/* 有参构造函数 */
MyString(const char* str);
/* Mystring 类的拷贝函数
* 深拷贝字符串char* */
MyString(const MyStirng&* my_str);
/* 析构函数 释放内存 */
~MyString();
/* 分为两种情况赋值操作
* 1 ,char* 类型
* 2 ,对象 类型的赋值操作 */
MyString& operator=(char* str);
/* 对象赋值的操作 */
MyString& operator=(const MyString& my_str);
/* 取出字符串中的index 的字符的操作 */
char operator[](const int index);
/* 字符串拼接的也两种情况
* 1 ,char* 拼接
* 2 ,对象类型拼接 */
MyString operator+(const char* str);
/* 字符串拼接
* 对象拼接的情况 */
MyString operator+(const MyString& my_str);
/* 比较两个是否相同分为两种情况
* 1 ,char* 比较
* 2 ,对象 比较 */
bool operator==(const char* str);
/* 对象的比较 */
bool operator==(const MyString& my_str);
protected:
private:
/* << 运算符重载打印 */
friend ostream& operator<<(const MyString& my_str);
/* >> 赋值的操作的 */
friend istream& operator >> (const MyString& my_str);
/* 字符串的大小 */
int m_size;
/* 字符串的地址 */
char* m_String;
};
#endif
3,继承的构造和析构, static
①先调用父类构造再调用子类构造,析构反置
②当子类成员函数和父类成员函数名相同时,子类会隐藏父类中成员函数。
解决的方法:加上作用域
③
4,多继承中虚继承使用,偏移量
继承中virtual相同的有在C11中以后override, final关键字了
class Animal
{
public:
int m_age;
};
class Sheep : virtual public Animal
{
};
class Tuo :virtual public Animal
{
};
class SheepTuo : public Sheep, public Tuo
{
};
void test01()
{
Sheep s;
s.m_age = 90;
s.Sheep::m_age = 100;
cout << s.m_age << endl;
}
int main(void)
{
test01();
system("pause");
return 0;
}
查看继承结构:
cl /d1 reportSingleClassLayout 类名
八 .多态,重载,重定义,重写
1.多态的继承,虚函数
① 在父类中要把可以重写的声明成虚函数或者纯虚函数子类要重写
②析构函数在父类中要声明 virtual类型子类要重写, 当声明成纯虚析构函数是要在类父类
的外写上析构函数
class Animal
{
public:
virtual void speack() = 0; // 纯虚函数成员
virtual ~Animal() = 0; // 纯虚析构函数
/*{
cout << "Animal 析构函数" << endl;
}*/
};
Animal::~Animal()
{
cout << "Animal 析构函数" << endl;
}
③虚机类表编译器内部原理
结构图
代码:
class Animal
{
public:
int m_age;
};
class Sheep : virtual public Animal
{
int m_a;
};
class Tuo :virtual public Animal
{
};
class SheepTuo : public Sheep, public Tuo
{
};
void test01()
{
Sheep s;
s.m_age = 90;
s.Sheep::m_age = 100;
cout << s.m_age << endl;
}
4.类型转换的安全性
①向上:是子类 -> 父类 动态是安全的
②向下 :父类 -> 子类 不安全的
5.重载 (同一作用域的同名函数 ),重定义 (隐藏 ),重写 (覆盖 )
①重载
- 同一个作用域
- 参数个数,参数顺序,参数类型不同
- 和函数返回值,没有关系
- const也可以作为重载条件 //do(const Teacher& t){} do(Teacher& t)
②重定义
1. 有继承
2. 子类(派生类)重新定义父类(基类)的同名成员(非virtual函数)
③ 重写(覆盖)
- 有继承
- 子类(派生类)重写父类(基类)的virtual函数
- 函数返回值,函数名字,函数参数,必须和基类中的虚函数一致
6.接口 interface
定义:
-
只有纯虚函数("=0")和静态函数(除了下文提到的析构函数) ,析构
函数不能是纯虚析构函数 -
没有非静态数据成员
-
没有定义任何构造函数,如果有,,也不能带有参数,并且必须为
protected -
如果它是一个子类,也只能从满足上述条件并以Interface为后缀的类继
承