c++primer plus 11章
1.不能返回局部变量或者临时变量的引用
2.运算符重载
之前在理解成员函数上出现了一点错误,这里改正
time operator+(const time& a )const //声明时
time time::operator+(const time& a )const {...} ;//定义时
B=A.operator(C) ; //顺序不可改
B=A+C ; //表达式复杂时要三思
之前出错是因为把返回类型放到了time::后面,这是不对的,因为这里要调用time中的成员函数operator+();
运算符重载的限制
@1不能改变标准类型,如用+来计算俩个数字之差;
@2不能把运算符重载为数字
@3不能改变运算符的优先级
@4特定运算符不支持重载,详见百度
“ . " , " .* " , " ?: " 书中P387 也可以查(支持重载目标)//?看书
@5只能通过成员函数重载运算符
3.友元函数
在类的公有部分声明
class a {
int b ;
public :
friend a operator*(double , a&) ; // 定义友元函数
};
a operator*(double m , a &t ) //声明,只要像正常的函数那样声明就行了
{ }
4.对运算符《的重载
friend void operator<<( ostream& os ,const time &t ) ;
//这种方法不能连续拼接
friend void& operator<<( ostream& os ,const time &t ) ;
//解决方法,返回ostream中cout的引用 ,使其可以连续拼接
//输出效果
cout<<time<<"end of file " <<endl;
//重载内涵:调用ostream中的cout函数来输出类中内容
//运算符可以二次重载,比如用来进行ofstream的输出输入
关于时间种子的使用
include<ctime> //srand();
include<cstdlib> //rand();
srand(time(null)) ; //最好用srand(time(0)) ;
rand()%100 ;// 0~100内随机一个数字
rand()%100-5 ; // -5~95内随机一个数字
rand(3) ; // 输出一个三阶矩阵,0~1内随机数字
365*rand(1,60) ; // 60个1~365之间随机正数
一般前两种够用了要用再查吧
伪随机
因为time(num) ;取得值每次都一样,都是固定区间的时间数
5.状态成员
即对数据进行整合的函数
6.有关于转换函数
stock(double a ):b(a ) ; //假如有构造
//在调用时
stock a = stock (19.6) ;//正常赋值
stock c = 20.8 ; // 隐式转换 , 不可以有二义性,只能传单参
//若要只允许显示转换
explicit stock(double a ):b(a ) ;
stock a = stock (19.6) ;//正常赋值,限定
有关转换函数
class ap {
int a = 1 ;
public :
operator int() const {return a ; } //定义函数的转化函数,无参,为类方法
} ;
int main()
{
ap d ;
int j = d ; //与下面的作用一样,但也调用了转换函数
cout << j <<endl ;
cout<<int(d) <<endl ; //==转换函数的调用==
return 0 ;
}
C++ primer plus 12 章
1.静态类成员可以在类外初始化
class ap {
static int a ;
} ;
int ap::a =1 ; //初始化类的静态成员
2.类的复制构造函数
一个内默认会定义构造函数,复制构造函数,析构函数,还有一些忘了,后面会有整理
类的默认复制构造函数只会传地址,所以如果new方法使用会产生错误
可以自己定义复制构造函数(深度复制)来解决
在数组中有成员函数strcpy可以使用
类中定义:
class ap {
int a ;
public :
ap &operator=(ap&) ; //一个自定义的深度复制
} ;
ap& ap::operator=(ap&d )
{
if ( &d == this )
return *this ;
a = d.a ;
return d;
}
//重载后的复制构造函数可能出现二义性
3.nullptr/0 初始化指针 ,前者为c++11标准
**4.重载[] **
这里不想在演示一遍了,就和一般的重载方法相同
有定义
char&string::operator(int i ) {return atr[i] ; }
调用时
string A[0]=‘a’ ; //来给a对象中元素赋值
5.静态成员
在类中定义的静态成员函数的调用方法
class ap {
public :
static int a ( int d ) { return d*2 ;}
} ;
int main()
{
int c = ap::a(3) ; //直接调用某个类的特定方法
return 0 ;
}
有关类的返回const和定位new方法和嵌套(递归)
略
没劲
对于:初始化方法
@1只能用于构造函数
@2只能初始化非静态非const的数据成员
@3必须用来初始化引用数据成员
@4C++11支持用()方法初始化和在私有成员定义时就初始化
如 int a(90) ; || private : a = 1 ;
6.初始化优先度
含参>不含参 ; 显示构造>默认构造
C++ primer plus 第13章
1.派生类与基类
1.1为了扩大类的使用,在基类上引申出了派生类,发挥作为基类的补充作用。
1.2
上代码
class ap {
int a ;
double b ;
bool c ;
char d ;
public :
ap (int ad , double bd , bool cd , char dd ):a(ad) , b(bd),c(cd) , d(dd){} ;
} ; //作死定义了一个超长的类
class k:public ap
{
char o ;
public :
k(int ad , double bd , bool cd , char dd ,char oo ):ap(ad,bd,cd,dd),o(oo) {} ;
} ; //突然感觉好简洁
int main()
{
k l = k (1,3.5,true,'f','g') ; //然后只要赋值k相当于ap也赋值了
return 0 ;
}
注意公有派生类内不能访问基类的私有成员
派生类的指针和引用可以调用基类方法,反之不可以
这种东西在用的时候自然就会了
写了一个出来当个样子
class ap {
int a ;
double b ;
bool c ;
char d ;
public :
ap (int ad , double bd , bool cd , char dd ):a(ad) , b(bd),c(cd) , d(dd){} ;
double myself (double m ) ;
} ;
double ap::myself( double m )
{
return m*a*b ;
}
class k:public ap
{
char o ;
public :
k(int ad , double bd , bool cd , char dd ,char oo ):ap(ad,bd,cd,dd),o(oo) {} ;
double ii (double oo )
{
return myself(9)*oo ; //调用基类方法
}
} ;
//如果在外面定义的话需要在使用基类方法是用域运算符,如
/** double k:: ii (double oo )
{
return ap::myself(9)*oo ; //调用基类方法
}
*/
int main()
{
k l = k (1,3.5,true,'f','g') ;
int lkj = l.ii(21) ;
cout<< lkj <<endl;
return 0 ;
}
2.is_a关系–详解公有继承类
即新类从基类中派生且在获取基类所有属性的同时增加了自身的一些属性
网上看到一种新奇的定义方法,哦莫昔洛依
Brass(const std::string &s = "Nullbody", long an = -1, double bal = 0.0);
Brass::Brass(const string &fullName, long acctNum, double balance)
{
this->fullName = fullName;
this->acctNum = acctNum;
this->balance = balance;
}
笑死我了,这人逼格真高
3.关于多态公有继承
@1 当基类和派生类需要使用一个方法的不同版本时
@2 virtual方法(虚函数):在需要使用的不同方法之前给关键词virtual,在调用时会根据不同的调用体来选择对应方法
@3 局限是只会无法用此类来访问基类的同名方法
@因为定义了虚构函数,所以加上虚析构函数~virtual 类名 //不管它有没有作用,加上无害,书上就是这么做的
4.关于联编
联编就是函数的调用
编译过程中完成的联编叫静态联编
运行过程中完成的联编叫动态联编
就是指多态,你两个版本的虚函数只有在运行时才知道你要调用哪种,一个选择性质的事情
5.强制转换
tql,这个大佬的举得例子我一看就懂
关于向上强制转换–即派生类指向基类的强制转换
class Base
{
public:
int b;
virtual void Test()
{
cout << "base" <<endl;
}
};
class Derived:public Base
{
public:
int d;
virtual void Test()
{
cout << "derived" <<endl;
}
};
int main()
{
Derived d;
Base b = d;//直接赋值(产生切割)
b.Test();
//输出效果 base
Base& b2 = d;//使用引用赋值(不产生切割)
b2.Test();
//输出效果 derived
Base* b3 = &d;//使用指针赋值(不产生切割)
b3->Test();
//输出效果 derived
return 1;
}
也就是说为了保留转换后函数的特征, 需要使用引用或指针来向上转换
关于向下强制转换–基类转换为派生类
Base *b = new Derived;
Derived *d = dynamic_cast<Derived*>(b);
if(!d)
{
cout << "dynamic cast err!"<<endl;
}
else
{
d->Test();
}
//输出效果--derived
向下强制转换也是为了不产生切割效果,从而输出转换后虚函数后的内容
切割:覆盖方法和子类数据丢失的现象生成切割(slice)
自己摸索出来的向上向下强制转换后的对象包含内容
1.向上强制转换
class Base
{
public:
int b;
virtual void Test()
{
cout << "base" <<endl;
}
void Test2()
{
cout << "kkk" <<endl;
}
};
class Derived:public Base
{
public:
virtual void Test()
{
cout << "derived" <<endl;
}
void Test3()
{
cout << "bbb" <<endl;
}
};
int main()
{
Derived d;
Base& b2 = d;//使用引用赋值(不产生切割)
b2.Test();
b2.Test2(); //这里输出的为基类的方法
//到这里全可以工作
b2.Test3();
/ **tmp/tmp21xpgh9e/src:41:7: 错误:‘class Base’ has no member named ‘Test3’; did you mean ‘Test’?
b3->Test3();
^~~~~ */
//报错,也就意味着向上转换后无法调用派生类方法
Base* b3 = &d;//使用指针赋值(不产生切割)
b3->Test();
b3->Test2();
b3->Test3();
//因为指针和引用效果等价不在赘述
return 0;
}
上面说明向上强制转换后对象只能调用基类的方法而不能调用派生类的方法,但是虚方法调用的时派生类的
向下强制转换
int main()
{
Base *b = new Derived;
Derived *d = dynamic_cast<Derived*>(b);
if(!d)
{
cout << "dynamic cast err!"<<endl;
}
else
{
d->Test();
d->Test2();//成功输出基类方法
d->Test3();//成功输出派生类方法
}
return 0;
}
基类向上强制转换之后相当于一个派生类的对象,不仅可以用基类和派生类普通方法,也可以用派生类定义的特有虚方法
总结:向上强制转换之后相当于一个用派生类虚方法的普通基类,向下强制转换后相当于一个本来的派生类
6.静态联编可以提高运行效率来节省内存
7.虚函数的定义
在不同类中定义的虚函数(形成)有不同的指针数组(地址表),在使用时相应访问(占内存)//所以推荐静态联编
8.虚函数不同于构造函数,其可以调用虚析构函数,详细我在上面的多态公有继承中讲到
虚函数的注意点:虚函数不能为友元(因为友元不为类方法)
关于重名函数:在派生类中出现重名函数(不是虚函数)会造成函数的重新定义,也就是说不允许重载,强制定义就是覆盖函数。
基类的函数随之被隐藏//这就是“返回类型协变”
9.在类的继承中,关键字protect终于展现出他的作用
保护访问控制
在类中定义关键字protect:模块,相当于半个private,其限制其中元素只能在派生类中被访问,也就是定义之后除了派生类,外部不能访问其中元素。
代码略,反之就是这个效果