C++学习记录
最简单的c++程序
#include <iostream>
int main()
{
std::cout << "Hello world!" << std::endl;
return 0;
}
标准输入输出流
可以使用
using namespace std;或者using std::cout;
控制字符,需要包含iomanip
setw--默认靠右输出,字符不够,左边补上空格
cout << setw(4) << 2 << endl;
setw函数演示
setw函数只对靠近自己的字符起作用
cout << setw(4) << 2 << 3 << endl;
setw函数的作用范围
hex,oct对之前出现的数字不起作用,但是对于之后出现的数字都起作用,也会改变后面的输出
cout <<12 << hex <<" "<< 16 << " "<< 12 << endl;
cout << oct << 16 << endl;
hex,oct控制符
setfill的作用,之前setw默认使用空格进行填充,setfill可以改变填充的字符,比如我换成'|',注意这里需要使用单引号
cout << setfill('|') << setw(10) << 2 << endl;
setfill('|')输出结果
输入相关
cin 可以使用空格,回车,制表符作为 间隔符
string类型
string 可以支持 运算符, >, <, ==, != ,
str.size() 相当于 strlen,也可以使用 str[n]的方式,str.empty();
getline(cin, str)
const char * p_str = str.c_str(); 这个可以获取字符串的地址,但是c++扩充的时候可能会重新释放,分配,所以使用需要注意
Vector类型
定义和初始化Vector类型Vector有很多操作他的方法
vec.push_back(), vec.size(), vec.pop_back
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
using std::cout;
using std::endl;
//using std::cin;
int main()
{
vector<string> v1;
string str;
int i = 0;
while (getline(cin, str)) {
if (str == "q") {
break;
}
v1.push_back(str);//将这个元素放进vector里面去, pop_back方法是弹出
}
cout << v1.size() << endl;
for (i = 0; i < v1.size(); i++) {
//str = v1.at(i);取出vector第几个元素出来
str = v1[i];
cout << i << ":" << str << ":" <<str.size() << endl;
}
return 0;
}
输出结果
引用
int &b = a; //b是a的别名
cout << "b's address:" << &b << endl;
cout << "a's address:" << &a << endl;
输出两个的地址,地址是相同的
image.png
引用必须在声明的时候就进行初始化,而且引用一旦与某个变量关联起来,就不会改变。
int a = 1;
int c = 20;
int *pt = &a;
cout << "pt's address:" << pt << endl;
int &b = *pt;
pt = &c;
cout << "pt's address:" << pt << endl;
cout << "c's address:" << &c << endl;
cout << "b's address:" << &b << endl;
cout << "a's address:" << &a << endl;
//b一开始声明为a的引用,即便pt后来指向了c,b仍然引用的是a
image.png
不同类型的变量不能互相引用,编译器会报错
int a = 2;
char &b = a;//invalid initialization of reference of type 'char&' from expression of type 'int'
有一种情况,函数的引用类型和传递过来的不对,会生成临时变量的方式
void func1(const int &tmp)
这种形式的好处
image.png
image.png
右值引用
image.png
函数的返回值如果是引用的话,怎么理解
#include <iostream>
using namespace std;
class sgy {
public:
sgy();
~sgy();
private:
string name;
int age;
static int number;
protected:
int pro_var;
};
sgy::sgy()
{
this->age = 0;
this->name = "NULL";
this->pro_var = 0;
cout << "sgy()" << endl;
}
sgy::~sgy()
{
cout << "~sgy()" << endl;
}
int &func2(int &a)
{
return a;
}
int main()
{
int a = 2;
cout << "a's address:" << &a << endl;
int &b = func2(a);
cout << "b's address:" << &b << " b=" << b << endl;
int c = func2(a);
cout << "c's address:" << &c << " c=" << c << endl;
a = 3;
cout << "a:" << a << " b:" << b << " c:" << c << endl;
return 0;
}
函数返回的是引用的话,接收的也只能是引用。所以b其实就是a的别名,但是c是独立的变量,
但是c的值是和a的值是一样的,后面即便更改了a的值,b的值会发生变化,但是c的值并不会改变。
image.png
如果上面的例子变成结构体会怎么样呢?
#include <iostream>
using namespace std;
class sgy {
public:
sgy();
~sgy();
private:
string name;
int age;
static int number;
protected:
int pro_var;
};
sgy::sgy()
{
this->age = 0;
this->name = "NULL";
this->pro_var = 0;
cout << "sgy()" << endl;
}
sgy::~sgy()
{
cout << "~sgy()" << endl;
}
struct exam {
int tmp1;
int tmp2;
};
exam & func(exam & exam1)
{
return exam1;
}
int main()
{
exam ex1;
ex1.tmp1 = 1;
ex1.tmp2 = 2;
exam ex2 = func(ex1);
cout << "ex2.tmp1:" << ex2.tmp1 << " ex2.tmp2:" << ex2.tmp2 << endl;
return 0;
}
如果返回的是引用而用结构体直接去接收的话,相当于是把结构体的副本数据接收了过来。看下面的运行结果,类也是一样。
image.png
如果是下面这种情况呢?ex1,ex2的结果是什么
exam &ex2 = func(ex1);//这是正确的使用方法
然后
exam ex1;
ex1.tmp1 = 1;
ex1.tmp2 = 2;
cout << "ex1's address:" << &ex1 << endl;
exam &ex2 = func(ex1);
cout << "ex2.tmp1:" << ex2.tmp1 << " ex2.tmp2:" << ex2.tmp2 << endl;
cout << "ex2's address:" << &ex2 << endl;
exam ex3;
ex3.tmp1 = 3;
ex3.tmp2 = 4;
cout << "ex3's address:" << &ex3 << endl;
ex2 = func(ex3);
cout << "***************** after func(ex3) ********************" << endl;
cout << "ex2.tmp1:" << ex2.tmp1 << " ex2.tmp2:" << ex2.tmp2 << endl;
cout << "ex2's address:" << &ex2 << endl;
image.png
可见引用不会改变,永远指向同一个人,用ex2 去接收一个返回值是引用的这种方式,也是按值传递,即把ex3的数据拷贝一份到ex2里面
函数返回是引用的时候的一些注意事项
函数的形参是使用const 修饰的情况
void func(const int &a);
这个时候的好处见上面的分析,适用于使用变量的地址,但是又不改变变量的值的情况下。
所以下面的语句会有问题
int &b = a;//const类型的引用不能赋值给常规引用
#const修饰函数返回值的注意事项,const返回值
不能使用常规引用来接收, 但是可以使用同类型的变量来接收,这个时候就是按值进行传递,只能使用const修饰的引用来接收
例如 const int &func(int a);
不能使用 int &a = func(b); 这个会报错。
但是可以使用 int a = func(b);
int &func(int a)---------这种情况下,返回的引用指向的是一个临时变量
{
return a;
}
基类引用可以指向派生类对象
这个可以参考一下,讲的挺有用的
image.png
- 函数可以有默认的参数
int func(int a=2, int b=3)
注意事项,当有一个变量设置了默认参数,其后的变量都需要有默认参数,否则会报错
int func(int i, int k = 2, int j);
3.函数重载
返回值不同,参数列表相同,不叫函数重载
- 函数模板
template <typename T>
T add(T a, T b)
{
return a + b;
}
函数模板只适用于函数实现相同,形参个数相同,但是函数重载就不限制个数
类和对象
类里面不指定属性的话,就是私有的成员,是不能访问的
class student {
int i;
int b;
};
int main()
{
class student st;
st.i = 0;//,error: 'int student::i' is private|,会报这个错误
st.b = 2;
return 0;
}
类内声明函数,类外定义应该怎么书写;
int class_name::func_name();
int student::get_age();//student 类的get_age函数
每新增加一个类,最好增加一个头文件和源文件的方式
构造函数和析构函数
构造函数没有返回值,函数名称和类名一样
下面是一个简单的构造函数的例子
sgy.h
#ifndef __SGY__H
class sgy {
public:
sgy();
void Print_age();
private:
int age;
};
#endif // __SGY__H
sgy.cpp
#include "sgy.h"
#include <iostream>
using namespace std;
void sgy::Print_age()
{
cout << age << endl;
}
sgy::sgy()
{
age = 20;
}
main.cpp
#include <iostream>
#include <iomanip>
#include <vector>
#include "sgy.h"
using namespace std;
int main()
{
sgy class_sgy;
class_sgy.Print_age();
return 0;
}
有参数的构造函数怎么写
sgy(int i_age);//sgy.h增加这个声明
/*sgy.c中增加这个构造函数的实现*/
sgy::sgy(int i_age)
{
age = i_age;
}
/*main.cpp里面这样使用*/
sgy class_sgy(10);
class_sgy.Print_age();
构造函数声明的时候,参数也是可以使用默认值的
sgy(int i_age = 20);//sgy.h增加这个声明
析构函数的执行顺序是怎么样的,它不能带任何参数,也没有返回值(包括void类型)
#include <iostream>
#include <iomanip>
#include <vector>
#include "sgy.h"
using namespace std;
sgy g_class_sgy(9);
int main()
{
sgy class_sgy(10);
sgy class_sgy1(11);
sgy class_sgy2(12);
return 0;
}
/*--------------------------------*/
sgy::~sgy()
{
cout << "age:" << age << endl;
}
执行析构函数的顺序是,先构造的对象越晚析构
下面这个例子更清楚一些
/*******************************************************/
#include <iostream>
#include <iomanip>
#include <vector>
#include "sgy.h"
using namespace std;
sgy g_class_sgy(9);
int main()
{
sgy class_sgy(10);
sgy class_sgy1(11);
sgy class_sgy2(12);
return 0;
}
/*******************************************************/
sgy::sgy(int i_age)
{
cout << "sgy->i_age:" << i_age << endl;
age = i_age;
}
sgy::~sgy()
{
cout << "~sgy->age:" << age << endl;
}
运行结果如下
this指针和复制构造函数,拷贝构造函数
this->类的成员 或者 *(this).类的成员
复制构造函数的执行时机
sgy.h
#ifndef __SGY__H
class sgy {
public:
sgy();
sgy(int i_age);
sgy(sgy &class_sgy);//复制构造函数
~sgy();
void Print_age();
private:
int age;
};
#endif // __SGY__H
sgy.cpp
#include "sgy.h"
#include <iostream>
using namespace std;
void sgy::Print_age()
{
cout << age << endl;
}
sgy::sgy()
{
age = 20;
}
sgy::sgy(int i_age)
{
cout << "sgy->i_age:" << i_age << endl;
age = i_age;
}
sgy::~sgy()
{
cout << "~sgy->age:" << age << endl;
}
sgy::sgy(sgy &class_sgy)
{
cout << "copy" << endl;
this->age = class_sgy.age;
}
main.cpp
#include <iostream>
#include <iomanip>
#include <vector>
#include "sgy.h"
using namespace std;
void func(sgy class_sgy)
{
cout << "func ecexute" << endl;
}
int main()
{
sgy sgy1(1);
func(sgy1);
return 0;
}
运行结果解释,先是执行sgy1的构造,然后执行func函数,执行了类的复制构造函数。
以类对象作为函数返回值的情况也会导致复制构造函数被调用
#include <iostream>
#include <iomanip>
#include <vector>
#include "sgy.h"
using namespace std;
sgy class_sgy(10);
void func(sgy class_sgy)
{
cout << "func ecexute" << endl;
}
sgy func1()
{
return class_sgy;
}
int main()
{
sgy sgy1;
//sgy1 = func1();
return 0;
}
以类作为返回值的时候,也会调用复制构造函数,或者拷贝构造函数
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
sgy(sgy &tmp);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
friend sgy operator++(sgy &tmp);
friend sgy operator++(sgy &tmp, int );
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor" << endl;
this->age = age;
this->name = name;
}
sgy::sgy(sgy &tmp)
{
cout << "copy func:" << __func__ << ", line:"<< __LINE__ << endl;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "print_info---this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
this->age = age;
this->name = name;
}
sgy operator++(sgy &tmp)
{
cout << "qqqq,func:" << __func__ << ", line:"<< __LINE__ << endl;
tmp.age += 1;
return tmp;
}
sgy operator++(sgy &tmp, int )
{
cout << "hhhhhh,func:" << __func__ << ", line:"<< __LINE__ << endl;
tmp.age += 1;
return tmp;
}
int main()
{
sgy sgy1(12, "it");
sgy1++;
//++sgy1;
sgy1.print_age_name();
return 0;
}
执行到后++的重载函数时,返回类,这时调用复制构造函数创建临时变量,然后又析构掉去
image.png
以类的引用作为返回,和以类直接作为返回的区别
/**************
这个不会导致复制构造函数被调用
*/
sgy & sgy_add(sgy &tmp)
{
return tmp;
}
/*****************************************
而下面这个会调用复制构造函数
*/
sgy sgy_add_exam(sgy &tmp)
{
return tmp;
}
那么什么时候返回引用,什么时候返回值呢?
m = ++p 可以返回引用,因为返回值和返回引用不影响结果的传递
m = p++;的话不能返回引用,否则m是增加过后的变量
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor******>>>>name:" << name << endl;
this->age = age;
this->name = name;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "print_info---this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
this->age = age;
this->name = name;
}
int main()
{
sgy sgy1(12, "it");
sgy sgy2 = sgy1;
return 0;
}
上面的代码有什么问题??
sgy1.name 指向的是"it", 而sgy2.name 指向的是同一块内存
运算符 = 号必须作为类内的成员函数来重载
下面的代码会有什么区别??
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
sgy(sgy &tmp);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
sgy & operator=(sgy &tmp);
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor******>>>>name:" << name << endl;
this->age = age;
this->name = name;
}
sgy::sgy(sgy &tmp)
{
cout << "copy func:" << __func__ << ", line:"<< __LINE__ << endl;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "print_info---this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
this->age = age;
this->name = name;
}
sgy & sgy::operator=(sgy &tmp)
{
cout << "operator=" << endl;
return tmp;
}
int main()
{
sgy sgy1(12, "it");
sgy sgy2 = sgy1;
sgy sgy3;
sgy3 = sgy1;
return 0;
}
sgy sgy2 = sgy1;----这个不会调用=号的运算符重载函数,而是调用复制构造函数
sgy sgy3;
sgy3 = sgy1;------而这个会调用=号的运算符重载函数
image.png
然后析构的顺序是和构造的顺序是相反的。
构造顺序
sgy1---sgy2---sgy3
析构顺序
sgy3---sgy2---sgy1
类的静态成员和静态成员函数
静态成员变量只能在类内声明,类内初始化会报错
private:
string name;
int age;
static int number = 0;
如果类内是这么声明的话,编译器报错
error: ISO C++ forbids in-class initialization of non-const static member 'sgy::number'|
所以需要在sgy.cpp 中进行定义,分配存储空间
int sgy::number = 0;
类的静态成员函数和变量差不多
一般用来访问类的静态成员变量,不能用来访问非静态成员变量和函数
int sgy::getnum()
{
set_age_name(11, "123");
return number;
}
会报如下错误
cannot call member function 'int sgy::set_age_name(int, std::string)' without object
const对象和const成员
这两个是等价的int main()
{
const sgy sgy1(12, "it");
sgy sgy2 = sgy1;
return 0;
}
这样写会报错,为什么呢,sgy1是const对象, 而复制构造函数
sgy::sgy(sgy &tmp)----所以不能把const对象转换为可读可写的引用
const不能调用非const 成员函数和变量
见下面的这个例子
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
sgy(const sgy &tmp);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
sgy & operator=(sgy &tmp);
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor******>>>>name:" << name << endl;
this->age = age;
this->name = name;
}
sgy::sgy(const sgy &tmp)
{
cout << "copy constructor, func:" << __func__ << ", line:"<< __LINE__ << endl;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "print_info---this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
this->age = age;
this->name = name;
}
sgy & sgy::operator=(sgy &tmp)
{
cout << "operator=" << endl;
return tmp;
}
int main()
{
const sgy sgy1(12, "it");
sgy sgy2 = sgy1;
sgy1.print_age_name();
return 0;
}
/*这个例子会报错
要改成这样 void sgy::print_age_name() const--实现的时候
void print_age_name() const;---声明的时候
*/
友元
friend void func();
void func()
{
sgy sgy1(10, "11");
sgy1.age = 12;
cout << sgy1.age << endl;
}
可以直接访问类的私有成员, 友元只能授予不能索取,还可以有友元类
运算符重载
运算符重载函数的格式类的内部作为成员函数实现的声明
sgy operator+(sgy& sgy_temp0);
两个类相加最终被翻译成现在这个样子
A.operator+(B);
/***************************** sgy.h ********************************/
#ifndef __SGY__H
#include <string>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
~sgy();
int set_age_name(int age, string name)
{
this->name = name;
this->age = age;
return 0;
}
void Print_age_name();
static int getnum();
friend void func();
sgy operator+(sgy& sgy_temp0);
private:
string name;
int age;
static int number;
};
#endif // __SGY__H
/***************************** sgy.cpp ********************************/
#include "sgy.h"
#include <iostream>
using namespace std;
int sgy::number = 0;
void sgy::Print_age_name()
{
cout << "age:" << age << ", name:" <<name<< endl;
}
sgy::sgy()
{
cout << "default constructor" << name;
age = 0;
name = "NULL";
number++;
cout << " " << "number:" << number << endl;
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name):" << name << endl;
this->age = age;
this->name = name;
}
sgy::~sgy()
{
number--;
cout << "~sgy->name:" << name << " number:" << number << endl;
}
int sgy::getnum()
{
//
return number;
}
sgy sgy::operator+(sgy &sgy_temp0)
{
string name;
int age;
cout << "*******" << sgy_temp0.name <<"*******" << this->name << endl;
age = this->age + sgy_temp0.age;
name = this->name + sgy_temp0.name;
sgy sgy1(age,name);
return sgy1;
}
/***************************** main.cpp ********************************/
#include <iostream>
#include <iomanip>
#include <vector>
#include "sgy.h"
using namespace std;
void func()
{
sgy sgy1(10, "11");
sgy1.age = 12;
cout << sgy1.age << endl;
}
int main()
{
sgy sgy0(1, "sg");
sgy sgy1(2, "y");
sgy sgy3(3, "&&");
sgy sgy2 = sgy0 + sgy1 + sgy3;
sgy2.Print_age_name();
return 0;
}
运行结果
运行结果
运算符重载的规则
可以重载的运算符
规则
重载单目运算符
int operator++() ------->这个相当于是++a;
int operator++(int)------>这个相当于是a++;
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
sgy(sgy &tmp);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
friend sgy operator++(sgy &tmp);
friend sgy operator++(sgy &tmp, int );
friend ostream & operator<<(ostream &output, sgy &tmp);
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor" << endl;
this->age = age;
this->name = name;
}
sgy::sgy(sgy &tmp)
{
cout << "copy func:" << __func__ << ", line:"<< __LINE__ << endl;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "print_info---this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
this->age = age;
this->name = name;
}
sgy operator++(sgy &tmp)
{
cout << "qqqq,func:" << __func__ << ", line:"<< __LINE__ << endl;
tmp.age += 1;
return tmp;
}
sgy operator++(sgy &tmp, int )
{
cout << "hhhhhh,func:" << __func__ << ", line:"<< __LINE__ << endl;
tmp.age += 1;
return tmp;
}
ostream & operator<<(ostream &output, sgy &tmp)
{
cout << "******** " << "age:" << tmp.age << ",name:" << tmp.name << " *************";
return output;
}
int main()
{
sgy sgy1(12, "it");
sgy1++;
//++sgy1;
sgy1.print_age_name();
return 0;
}
上面的代码可以直接运行
重载流插入,流提取运算符
这两个运算符只能作为普通函数,不能作为类的成员函数实现
image.png
下面写几个关于流运算符重载的例子,流运算符只能用作普通函数
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
sgy(sgy &tmp);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
friend ostream & operator<<(ostream &output, sgy &tmp);
friend istream & operator>>(istream &input, sgy &tmp);
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor******>>>>name:" << name << endl;
this->age = age;
this->name = name;
}
sgy::sgy(sgy &tmp)
{
cout << "copy func:" << __func__ << ", line:"<< __LINE__ << endl;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "print_info---this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
this->age = age;
this->name = name;
}
ostream & operator<<(ostream &output, sgy &tmp)
{
cout << "operator<< overload " << "age:" << tmp.age << ",name:" << tmp.name << " *************" << endl;
return output;
}
istream & operator>>(istream &input, sgy &tmp)
{
cin >> tmp.age >> tmp.name;
return input;
}
int main()
{
sgy sgy1(12, "it");
cin >> sgy1;
cout << sgy1;
return 0;
}
运行结果
image.png image.png
运算符重载本质上也是一个函数,也可以直接调用
类的内部也可以定义重载函数,不过格式就不一样了
类的内部的函数,一般都是p.operator+(),所以就不需要传递两个参数了
类型转换
image.png image.png继承与派生
继承和派生的格式
image.png接下来是一个实际的例子
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
//sgy(sgy &tmp);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
const sgy & sgy_add(sgy &tmp);
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor******>>>>name:" << name << endl;
this->age = age;
this->name = name;
}
#if 0
sgy::sgy(sgy &tmp)
{
cout << "copy constructor, func:" << __func__ << ", line:"<< __LINE__ << ", name:" << tmp.name << endl;
}
#endif
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "print_info---this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
this->age = age;
this->name = name;
}
class sgy_inherit : public sgy {
};
int main()
{
sgy_inherit sgy1;
sgy1.set_age_name(12, "it");
sgy1.print_age_name();
return 0;
}
class sgy_inherit : public sgy {
};//表示sgy_inherit 从 sgy继承而来
1. 派生类不能够访问基类的私有成员
下面这样的写法是会报错的,因为sgy_inherit 的类内成员函数访问不了基类的私有成员。
class sgy_inherit : public sgy {
int get_age();
};
int sgy_inherit::get_age()
{
return this->age;
}
派生类的类内成员函数可以访问基类的protected成员,不能直接在类外进行访问,类似于 class_xxx.var 的形式
派生类可以调整由基类继承过来的变量的权限,派生类可见的变量和函数的权限
使用下面的方式即可,对private不可以这么做
class sgy_inherit : public sgy {
private:
public:
using sgy::protect_var;
};
也可以将函数的权限进行调整
class sgy_inherit : public sgy {
private:
using sgy::print_age_name;
public:
using sgy::protect_var;
};
访问权限的总结
image.png看下面的实际例子
class sgy_inherit_public : public sgy {
};
class sgy_inherit_protected : protected sgy {
};
class sgy_inherit_private : private sgy {
};
int main()
{
sgy_inherit_public sgy_public;
sgy_inherit_protected sgy_protected;
sgy_inherit_private sgy_private;
sgy_public.print_age_name();
//sgy_protected.print_age_name();//protected不能在类的外部直接使用
//sgy_private.print_age_name();//private继承下来还是private的,所以也不能直接访问
return 0;
}
派生类可以重新实现基类的函数
class CCurrentTime : public CTime 派生类的声明
派生类的方法和基类的方法同名的时候会优先调用派生类的方法
class sgy_inherit_public : public sgy {
public:
void print_age_name();
};
/*这里把基类的这个函数重写了,不会调用基类的函数,而是调用派生类的这个函数*/
void sgy_inherit_public::print_age_name()
{
cout << "inherit func overrite" << endl;
}
int main()
{
sgy_inherit_public sgy_public;
sgy_public.print_age_name();
return 0;
}
派生类是一个基类对象,可以将派生类直接赋值给基类
多重继承
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class CSofa {
public:
void WatchTv() {
cout << "func:" << __func__ << endl;
}
};
class CBed {
public:
void Sleep() {
cout << "func:" << __func__ << endl;
}
};
class CSofaBed: public CSofa, public CBed {
};
int main()
{
CSofaBed CSofaBed1;
CSofaBed1.WatchTv();
CSofaBed1.Sleep();
return 0;
}
输出结果
image.png
虚拟基类继承
虚拟基类的构造函数只会执行一次
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class CFurniture {
private:
int m_iWeight;
public:
void m_SetWeight(int iWeight) { this->m_iWeight = iWeight; }
int m_GetWeight(void) const { return this->m_iWeight; }
};
class CSofa : virtual public CFurniture{
public:
void WatchTv() {
cout << "func:" << __func__ << endl;
}
};
class CBed : virtual public CFurniture {
public:
void Sleep() {
cout << "func:" << __func__ << endl;
}
};
class CSofaBed: public CSofa, public CBed {
};
int main()
{
CSofaBed CSofaBed1;
CSofaBed1.WatchTv();
CSofaBed1.Sleep();
CSofaBed1.m_SetWeight(100);
cout << CSofaBed1.m_GetWeight() << endl;
return 0;
}
构造函数的执行顺序,构造函数顺序
image.png实例展示
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class CFurniture {
private:
int m_iWeight;
public:
void m_SetWeight(int iWeight) { this->m_iWeight = iWeight; }
int m_GetWeight(void) const { return this->m_iWeight; }
CFurniture() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class CVertification3C {
public:
CVertification3C() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class CSofa : virtual public CFurniture, virtual public CVertification3C{
public:
void WatchTv() {
cout << "func:" << __func__ << endl;
}
CSofa() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class CBed : virtual public CFurniture, virtual public CVertification3C {
public:
void Sleep() {
cout << "func:" << __func__ << endl;
}
CBed() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class CSofaBed: public CSofa, public CBed {
public:
CSofaBed() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class CLeftRightCom {
public:
CLeftRightCom() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class Date {
public:
Date() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class Type {
public:
Type() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
class CLeftRightSofabed : public CSofaBed, virtual public CLeftRightCom {
private:
Date date;
Type type;
public:
CLeftRightSofabed() {
cout << "func:" << __func__ << " constructor" << endl;
}
};
int main()
{
CLeftRightSofabed s;
return 0;
}
执行结果
func:CFurniture constructor
func:CVertification3C constructor
func:CLeftRightCom constructor
func:CSofa constructor
func:CBed constructor
func:CSofaBed constructor
func:Date constructor
func:Type constructor
func:CLeftRightSofabed constructor
多重继承怎么给构造函数传递参数
如果是父类就写类名,如果是子类就写变量的名字
LeftRightSofabed(char *str1, char *str2, char *str3) : Sofabed(str1), LeftRightCom(str2), date(str3) { cout <<"LeftRightSofabed()"<<endl; }
三种属性的比较
第一: private,public,protected的访问范围:
private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问.
protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问
public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问
注:友元函数包括两种:设为友元的全局函数,设为友元类中的成员函数
第二:类的继承后方法属性变化:
使用private继承,父类的所有方法在子类中变为private;
使用protected继承,父类的protected和public方法在子类中变为protected,private方法不变;
使用public继承,父类中的方法属性不发生改变;
对于同一类
protected成员不允许类对象直接访问,和private一样。
#include <iostream>
using namespace std;
class sgy {
public:
sgy();
~sgy();
private:
string name;
int age;
static int number;
protected:
int pro_var;
};
sgy::sgy()
{
this->age = 0;
this->name = "NULL";
this->pro_var = 0;
cout << "sgy()" << endl;
}
int main()
{
sgy sgy1;
sgy1.pro_var = 0;//这样写会报错,sgy::pro_var' is protected
return 0;
}
多态
多态为什么会出现
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class CHuman {
public:
void m_Eating() {
cout << "func:" << __func__ << ", CHuman" << endl;
}
};
class CEnglishMan : public CHuman {
public:
void m_Eating() {
cout << "func:" << __func__ << ", CEnglishMan" << endl;
}
};
class CChineseMan : public CHuman {
public:
void m_Eating() {
cout << "func:" << __func__ << ", CChineseMan" << endl;
}
};
void TestEating(CHuman &h)
{
h.m_Eating();
}
int main()
{
CHuman h;
CEnglishMan e;
CChineseMan c;
TestEating(h);
TestEating(e);
TestEating(c);
return 0;
}
执行结果
func:m_Eating, CHuman
func:m_Eating, CHuman
func:m_Eating, CHuman
改成这样声明之后,
class CHuman {
public:
virtual void m_Eating() {
cout << "func:" << __func__ << ", CHuman" << endl;
}
};
执行结果
func:m_Eating, CHuman
func:m_Eating, CEnglishMan
func:m_Eating, CChineseMan
使用func(a++);传递的时候,传过来的值是多少
a = 2, func接收到的值也是2
内联函数与宏的区别
内联函数还是按照函数的按值传递,宏是直接按文本进行替换
#define square(x) x*x
inline dobule square(dobule x);
同样是square(4.5+7.5),inline 计算的是12的平方,而宏则是 4.5+7.5 * 4.5+7.5
new 运算符
int *ptr = new int [10];
delete [] ptr
int *p_i = new int (6);//将其初始化为6
image.png
使用new和delete的注意事项
image.png
第9章 内存模型和名称空间
声明区域和潜在作用域
image.png image.png image.png
using声明和using编译指令
using声明将特定的名称添加到它所属的声明区域中。
#include <stdio.h>
namespace sgy {
int sgy11;
};
using sgy::sgy11;
int sgy11;
int main()
{
return 0;
}
但是像上面那样定义是有问题的,不能都放在全局的命名空间中
可以向下面这样使用。
#include <stdio.h>
namespace sgy {
int sgy11;
};
int sgy11 = 3;
int main()
{
using sgy::sgy11;
sgy11 = 2;
printf("sgy11:%d, 全局sgy11:%d\n", sgy11, ::sgy11);
return 0;
}
输出结果如下,引用全局变量可以使用::var的方法
sgy@ubuntu:~/sgy/user_program/c++$ ./test
sgy11:2, 全局sgy11:3
sgy@ubuntu:~/sgy/user_program/c++$
using namespace sgy----using编译指令会使名称空间中所有的名称可用。但是不会引起报错,如果名称空间中有同名的变量。
#include <stdio.h>
namespace sgy {
int sgy11 = 100;
};
//int sgy11 = 3;
int main()
{
using namespace sgy;
int sgy11 = 3;
//sgy11 = 2;
printf("sgy11:%d\n", sgy11);
printf("namespace sgy::sgy11:%d\n", sgy::sgy11);
return 0;
}
上面使用using namespace sgy; 虽然名称空间中sgy也有一个变量,但是这样不会引起名称冲突,但是如果使用的是 using sgy::sgy11; 这样的方式将会报错
注意:
image.png
#include <stdio.h>
namespace sgy {
int sgy11 = 100;
};
int sgy11 = 40;
using namespace sgy;
int main()
{
int sgy11 = 3;
//sgy11 = 2;
printf("sgy11:%d\n", sgy11);
printf("sgy11:%d\n", ::sgy11);
printf("namespace sgy::sgy11:%d\n", sgy::sgy11);
return 0;
}
上面的这个例子足以说明效果,输出结果见下方
sgy@ubuntu:~/sgy/user_program/c++$ ./test
sgy11:3
sgy11:40
namespace sgy::sgy11:100
名称空间的嵌套和名称空间的别名
image.png
未命名的名称空间
C++类构造函数初始化列表
问题和注意事项
1. sgy sgy2(); 这是一个函数声明,不是一个类的实例化,
例如下面的程序输出结果
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
~sgy();
void set_age_name(int age, string name);
void print_age_name(int age, string name);
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor" << endl;
this->age = age;
this->name = name;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name(int age, string name)
{
cout << "this->name:" << this->name << "this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
this->age = age;
this->name = name;
}
int main()
{
sgy sgy1(12, "it");
sgy *ptr = &sgy1;
sgy *ptr1 = &sgy1;
sgy sgy2();
return 0;
}
image.png
可见 sgy sgy2(); 并没有调用构造函数
//备份一部分代码
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
class sgy {
public:
sgy();
sgy(int age, string name);
~sgy();
void set_age_name(int age, string name);
void print_age_name();
friend void operator++(sgy &tmp);
friend void operator++(sgy &tmp, int );
private:
int age;
string name;
};
using namespace std;
sgy::sgy()
{
cout << "default constructor" << endl;
this->age = 0;
this->name = "default";
}
sgy::sgy(int age, string name)
{
cout << "sgy(int age, string name) constructor" << endl;
this->age = age;
this->name = name;
}
sgy::~sgy()
{
cout << "~sgy: " << "this->name:" << this->name << endl;
}
void sgy::print_age_name()
{
cout << "this->name:" << this->name << " this->age:" << this->age << endl;
}
void sgy::set_age_name(int age, string name)
{
this->age = age;
this->name = name;
}
void operator++(sgy &tmp)
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
tmp.age += 1;
}
void operator++(sgy &tmp, int )
{
cout << "func:" << __func__ << ", line:"<< __LINE__ << endl;
tmp.age += 1;
}
int main()
{
sgy sgy1(12, "it");
sgy1++;
//++sgy1;
sgy1.print_age_name();
return 0;
}
delete 与 delete[] 区别:
1、针对简单类型 使用 new 分配后的不管是数组还是非数组形式内存空间用两种方式均可 如:
int *a = new int[10];
delete a;
delete [] a;
此种情况中的释放效果相同,原因在于:分配简单类型内存时,内存大小已经确定,系统可以记忆并且进行管理,在析构时,系统并不会调用析构函数, 它直接通过指针可以获取实际分配的内存空间,哪怕是一个数组内存空间(在分配过程中 系统会记录分配内存的大小等信息,此信息保存在结构体_CrtMemBlockHeader中, 具体情况可参看VC安装目录下CRT\SRC\DBGDEL.cpp)
2、针对类Class,两种方式体现出具体差异
当你通过下列方式分配一个类对象数组:
class A
{
private:
char *m_cBuffer;
int m_nLen;
public:
A(){ m_cBuffer = new char[m_nLen]; }
~A() { delete [] m_cBuffer; }
};
A *a = new A[10];
// 仅释放了a指针指向的全部内存空间 但是只调用了a[0]对象的析构函数 剩下的从a[1]到a[9]这9个用户自行分配的m_cBuffer对应内存空间将不能释放 从而造成内存泄漏
delete a;
// 调用使用类对象的析构函数释放用户自己分配内存空间并且 释放了a指针指向的全部内存空间
delete [] a;
所以总结下就是,如果ptr代表一个用new申请的内存返回的内存空间地址,即所谓的指针,那么:
delete ptr -- 代表用来释放内存,且只用来释放ptr指向的内存。
delete[] rg -- 用来释放rg指向的内存,!!还逐一调用数组中每个对象的 destructor!!
对于像 int/char/long/int*/struct 等等简单数据类型,由于对象没有 destructor,所以用 delete 和 delete [] 是一样的!但是如果是C++ 对象数组就不同了!
问题:
有 const 修饰的成员函数(指 const 放在函数参数表的后面,而不是在函数前面或者参数表内),只能读取数据成员,不能改变数据成员;没有 const 修饰的成员函数,对数据成员则是可读可写的。
成员初始化列表
sgy::sgy(int age, int index) : age(age), index(index)
隐式转换发生的时机
image.png
const 指针和const 修饰的变量
image.png
双冒号(::)用法
-
作用域限定符,当在类体中直接定义函数时,不需要在函数名字的前面加上类名,但是在类体外实现函数定义的时候,必须加上类名并且加上作用域限定符。Student::Display();
-
静态数据成员既可以通过对象名引用,也可以通过类名加::来引用
-
静态成员函数,也是既可以通过对象名引用,也可以通过类名+::引用。
感觉这篇文章写得挺好的
https://blog.csdn.net/lixungogogo/article/details/51118524
C++ 子类对象当父类对象使用, 给下面的print函数加上virtual,还只能加在父类里面才管用
#include <iostream>
using namespace std;
class Base
{
public:
int a;
Base()
{
a = 0;
cout << "I`m base Begin" << endl;
}
void Print()
{
cout << "father: a=" <<a << endl;
}
~Base()
{
cout << "I`m base End" << endl;
}
};
class Simple : public Base
{
public:
int a;
Simple()
{
a = 1;
cout << "I`m Simple Begin" << endl;
}
virtual ~Simple()
{
cout << "I`m Simple End" << endl;
}
void Print()
{
cout<< "child: a=" <<a<<endl;
}
};
int main()
{
Simple b;
Base *pBase = &b;//多态看的是指针所指向的类型来调用
b.Print();//这个地方是simple 也就是child 1
cout << b.a << endl;
cout << pBase->a << endl;
pBase->Print();
return 0;
}
Base *pBase = &b;//多态看的是指针所指向的类型来调用
如果不是虚函数的话,调用的是base->print, 如果是多态的话,调用的是指向的类型的print函数
1 前言
记得当初阿里面试的时候被问道这个问题,平时自己面对这个方法都习以为常的使用,C++多态和动态绑定不就是这么实现的嘛,但是还真没有刻意去关注其中的原理。今天特意看了相关资料,现在分享给大家。
其实这块我们需要分为两种情况来考虑,第一种是类继承(静态绑定),第二种是父类中包含虚函数(动态绑定)。
2 具体实现
2.1 没有虚函数的继承
如果以一个基础类指针指向一个派生类对象,那么经由该指针只能访问基础类定义的函数(静态绑定)。
如果以一个派生类类指针指向一个基础类对象,必须先做强制转型动作(explicit cast),这种做法很危险,也不符合生活习惯,在程序设计上也会给程序员带来困扰。(一般不会这么去使用)
如果基础类和派生类类定义了相同名称的成员函数,那么通过对象指针调用成员函数时,到底调用那个函数要根据指针的原型来确定,而不是根据指针实际指向的对象类型确定。(指针类型是谁就调用谁)
这块我需要说明一下为何基础类可以指向派生类对象,而派生类不去指向父类对象。
通常来说,子类总是含有一些父类没有的成员变量,或者方法函数。而子类肯定含有父类所有的成员变量和方法函数。所以用父类指针指向子类时,没有问题,因为父类有的,子类都有,不会出现非法访问问题。
但是如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量,就会出现非法,因为被子类指针指向的由父类创建的对象,根本没有要访问的那些内容,那些是子类特有的,只有用子类初始化对象时才会有。
2.2 包含虚函数的继承
有虚函数的继承,那么父类指针指向子类对象就是我们常见的多态实现,也就是动态绑定。
虚拟函数就是为了对“如果你以一个基础类指针指向一个衍生类对象,那么通过该指针,你只能访问基础类定义的成员函数”这条规则反其道而行之的设计。
当然这里还包括纯虚拟函数,只要是拥有纯虚拟函数的类,就是抽象类,它们是不能够被实例化的(只能被继承)。如果一个继承类没有改写父类中的纯虚函数,那么他也是抽象类,也不能被实例化。抽象类不能被实例化,不过我们可以拥有指向抽象类的指针,以便于操纵各个衍生类。
2 总结
当定义一个指向子类实例的父类指针的时候,内存中实例化了子类,由于子类继承了父类,因此内存中的子类里包含父类的所有成员。但由于生命的时父类指针,因此该指针不能够访问子类的成员,而只能访问父类的成员。然而在父类里可以声明纯虚函数和定义虚函数,使用父类指针访问虚函数或纯虚函数的时候,访问到的是子类里重写的函数。当然,对于虚函数,如果子类里没有对其重写的话,仍然访问到父类里定义的虚函数。可见虚函数和纯虚函数的却别仅仅在于:纯虚函数没有定义,只有声明。
C++模板
C++ 模板详解(一)
注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
类模板演示的实际例子
template<class T> class A{//类模板的声明写什么样子
public:
T g(T a,T b);
A();
};
template<class T> A<T>::A(){}
template<class T> T A<T>::g(T a,T b){
return a+b;
}
int main(){
A<int> a;//类模板的实例化,所有用到T的地方全部都用int代替
cout<<a.g(2,3.2)<<endl;
}
类模板定义好之后,怎么定义类外的成员函数
4、在类模板外部定义成员函数的方法为:
template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},
比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:
template<class T1,class T2> void A<T1,T2>::h(){}。
强制类型转换
C++四种类型转换运算符:static_cast、dynamic_cast、const_cast和reinterpret_cast
C++ Template 基础篇(一):函数模板
虚拟继承为什么要出现
#include <iostream>
using namespace std;
class A {
public:
int m_a;
};
//直接基类B
class B: public A{
public:
int m_b;
};
//直接基类C
class C: public A{
public:
int m_c;
};
//派生类D
class D: public B, public C{
public:
void seta(int a){ m_a = a; } //命名冲突,这个地方可能从B继承来的m_a, 也有可能是从C继承来的m_a
};
int main()
{
D d;
return 0;
}
子类继承父类之后,如果有同名的函数或者变量,首先访问的是子类的同名成员函数或者变量
#include <iostream>
#include <stdio.h>
using namespace std;
class A {
public:
int m_a;
};
//直接基类B
class B: public A{
public:
int m_b;
B() {
m_a = 2;
printf("B的构造函数\n");
}
void print() {
m_a = 3;
printf("现在调用的是B里面的print函数, m_a = %d\n", m_a);
}
};
//直接基类C
class C: public A{
public:
int m_c;
C() {
m_a = 4;
printf("c的构造函数\n");
}
};
//派生类D
class D: public B, public C{
public:
//void seta(int a){ m_a = a; } //命名冲突,这个地方可能从B继承来的m_a, 也有可能是从C继承来的m_a
void print();
};
void D::print() {
B::print();
printf("B::m_a:%d, C::m_a:%d\n", B::m_a, C::m_a);
}
int main()
{
D d;
d.print();
d.B::print();//显示的调用B里面的print函数
return 0;
}
比如上面 D继承于B, B和D里面都有print函数,那么D怎么能够使用到B里面的成员函数呢?
- B::print----->这个是在类的内部使用的,
- d.B::print---->这个是在类的外部使用的方式
下面这个函数访问的是子类的同名函数而不会访问父类里面的
#include <iostream>
#include <stdio.h>
using namespace std;
class A {
public:
int m_a;
};
//直接基类B
class B: public A{
public:
int m_b;
B() {
m_a = 2;
printf("B的构造函数\n");
}
void print() {
m_a = 3;
printf("现在调用的是B里面的print函数, m_a = %d\n", m_a);
}
};
//直接基类C
class C: public A{
public:
int m_c;
C() {
m_a = 4;
printf("c的构造函数\n");
}
};
//派生类D
class D: public B, public C{
public:
//void seta(int a){ m_a = a; } //命名冲突,这个地方可能从B继承来的m_a, 也有可能是从C继承来的m_a
void print();
void print1() {
print();
}
};
void D::print() {
//B::print();
printf("D类, B::m_a:%d, C::m_a:%d\n", B::m_a, C::m_a);
}
int main()
{
D d;
d.print1();
//d.B::print();//显示的调用B里面的print函数
return 0;
}
使用函数默认参数有哪些需要注意的事项
#include <stdio.h>
class sgy {
public:
int age;
sgy();
sgy (int age = 12);
};
sgy::sgy()
{
printf("默认的构造函数\n");
}
sgy::sgy(int age)
{
printf("sgy(int age)构造函数\n");
}
int main()
{
sgy sgy1;
return 0;
}
上面的代码会报错,报错的原因是因为不知道使用哪一个构造函数, 因为默认参数指定的情况下,两个函数都满足情况,所以不知道调用哪一个,报错。
root@iZ2ze9yqb3xdqngyx445idZ:~/sgy/test# g++ -o main main.cpp
main.cpp: In function ‘int main()’:
main.cpp:110:9: error: call of overloaded ‘sgy()’ is ambiguous
sgy sgy1;
^
main.cpp:102:1: note: candidate: sgy::sgy(int)
sgy::sgy(int age)
^
main.cpp:97:1: note: candidate: sgy::sgy()
sgy::sgy()
^
root@iZ2ze9yqb3xdqngyx445idZ:~/sgy/test#