c++ plus复习(一)
运算符重载
// 用于 if(obj) 中的obj的判断
operator void*()
{
return (void*)0; // or return (void*)1;
}
类型转换重载
operator typeName(); 虽然该函数没有声明返回类型,但应返回所需类型的值。使用转换函数时要小心,可以在声明构造函数时使用关键字explicit,以防止它被用于隐式转换。
函数默认值要在调用之前
变量名是内存的别名,引用是变量的别名;引用之于被引用对象犹如变量名之于内存。
// 严重错误,const引用临时变量,函数结束时该临时变量将不存在
const string& fun()
{
string tmp;
return tmp;
}
函数,模板具体化,模板都可以重载
模板实例化:
具体化:
void add(int a, int a){}
template<typename T>
void add(T a, T b){}
template<>
void add(int a, int b){}
template<>
void add<int>(int a, int b){}
add(1.1, 2.1);
add<float>(1.1, 2.1);
decltype
decltype(expression) var;
trailing return type
template<class T1, class T2>
auto gt(T1 x, T2 y) -> decltype(x + y)
{
return x + y;
}
…
head.h
#ifndef HEAD_H_
#define HEAD_H_
int fun(int a, int b)
{ return a+b; }
#endif
test1.cpp
#include // 从默认系统库中搜索
#include "head.h" // 从当前目录下搜索头文件
void fun2(int, int);
int main(int argc, const char** argv)
{
int a = 0, b = 1;
fun(a, b);
fun2(a, b)
}
test2.cpp
#include "head.h"
int fun2(int a, int b)
{ fun(1, 2); return a+b; }
以上在连接时将报错:
multiple definiton of ‘func(int, int)’
在两个.cpp文件中同时存在fun函数的定义
解决方法
int a;
全局变量,可以被其它文件引用static int a;
只能在本文件内引用{static int a;}
局部静态变量,只能在局部引用#include
int x; // 0初始化
int y = 5; // 常量表达式初始化
long z = 13 * 13; // 常量表达式初始化
const double pi = 4.0 * atan(1.0); // 动态初始化
函数
C++函数的作用域可以是整个类或整个命名空间(包括全局的),但不能是局部的(因为不能在代码块内定义函数,如果函数的作用域为局部,则只能对它自己是可见的,因此不能被其他函数调用。这样的函数将无法运行)。
外部链接性的函数:
extern return_type fun_name(parameter list); // extern是可选项
内链接性:
static int private_f(double x);
static int private_f(double x)
{
...
}
如果在程序的某个文件中调用一个函数。如果该函数是静态的,则在该文件中找;如果不是静态的则在全文件中找,若找到连个两个定义则报错;如果都没找到,则在库中搜索。如果定义了与库函数同名的函数,则会使用用户定义的函数。
变量
变量是如何创建的(由编译器自动创建还是手动new),变量数据的存储位置,变量的作用域,链接性
regitster
关键字register最初是由C语言引入的,它建议编译器使用CPU寄存器来存储自动变量
register int count_fast; // request for a register variable
这为了提高变量的访问速度。
如今在C++中此关键字几乎无用,保留主要是为了兼容旧代码。
volatile
告诉编译器不要对变量进行优化。
mutable
即使类变量为const,某个成员也可以被修改。
struct data{
char name[30];
mutable int accesses;
...
};
const data veep = {"abc", 3};
veep.accesses = 30; // allowed
const
在C++中,const限定符对全局变量的默认作用范围进行了修改。
const int cina = 10;
实际上是static的,即只能在本文件中使用,可 以通过包含const头文件的方式使用,因为是const的所以cosnt是否是外部或内部不影响。
可以通过extern const int states = 50;
声明外部链接
extern “C”
g++编译器将以C的语法编译此代码,即fun函数不会编译成fun_i_c_而是_fun
通常我们将类的声明放在.h文件中去,而将实现放到.cpp中去。
调用默认构造函数时不要加(),因为像函数调用。
类中定义的成员函数被所有对象所共用,通过this指针确定对象。
static成员函数没有this指针。
int main(int argc, const char** argv)
{
Clas() = default; // 纯种默认构造函数,显示的让编译器生成,否则编译器不生成
Clas str("default construct"); // 默认构造
Clas str2; // 默认构造
Clas str3 = Clas("default construct"); // 默认构造(可能会创建临时对象,也可能不会,我这里不会)
str2 = str3; // operator=
str2 = Clas("tmp then operator="); // 先用默认构造出一个临时对象,再operator=
// 临时对象使用完后立即析构,有些编译器也可能过会在析构
// 构造顺序 str str2 str3,析构顺序与之相反
}
如果对象在初始化时,是通过其他对象初始化的则调用拷贝构造函数,否则调用默认构造函数
ClassType obj(…); ClassType obj = ClassType(…); // 默认构造
ClassType obj(otherobj); 等价于 ClassType obj = otherobj; // 拷贝构造
const Cls str1 = {}; // 列表初始化
...
const Cls& show(const Cls& sr) const;
// 括号中的const表明不修改sr对象,括号外的const表明不修改*this
// 类似const Cls& show(const Cls* this, const Cls& sr);
// 类似 str.show() <=> show(&str)
const CLASS a = {};
a.show(); // show必须是 return_type show()const;
声明类指是描述了对象的形式,并没有创建对象,在创建对象前,将没有用于存储值的空间。
class Bakery
{
private:
const int Months = 12; // 行不通
double costs[Months];
...
};
// 两种方法解决
// 1.
// 用这种方式声明枚举并不会创建类数据成员,所有对象中都不包含枚举。Months只是
// 一个符号名称,在作用域为整个类的代码中遇到它时,编译器将用30来替换它
class Bakery
{
private:
enum {Months = 12};
double costs[Months];
...
};
// 2.
// 创建static常量,存储在静态区,可被所有Bakery对象共享
class Bakery
{
private:
static const int Months = 12;
double costs[Months];
...
};
类内枚举
// 传统枚举如果枚举量的名称相同将发生冲突
// 类内枚举
enum egg_old {SMALL, MEDIUM, LARGE}; // unscoped
enum class t_shirt {SMALL, MEDIUM, LARGE}; // scoped
egg_old one = SMALL; // unscoped
t_shirt rolf = t_shirt::MEDIUM; // scoped
int king = one; // implicit type conversion for unscoped
int ring = rolf; // × not allowed, no implicit type conversion
if (king < LARGE) // allowed
std::cout << "LARGE converted to int before comparison.\n";
if (king < t_shirt::MEDIUM) // × not allowed
std::cout << "Not allowed:< not defined for scoped enum.\n";
// 显示类型转换
int Frodo = int(t_shirt::SMALL); // Frodo set to 0
// 显示指定枚举类的底层依赖
enum class : short pizza {SMALL, MEDIUM, LARGE};
static
int ClassName::static_number = 0;
这条语句将静态成员static_number的值初始化为0 。不能在类声明中初始化静态成员变量,这是因为声明表述了如何分配内存,但并不分配内存。可以使用这种格式ClassName obj;来创建对象,从而分配和初始化内存。对于静态类成员,可以在类声明之外使用单独的语句来进行初始化,这是因为静态类成员是单独存储的,而不是对象的组成部分。初始化语句指出了类型,并使用了作用域运算符,但没有使用关键字static 。
初始化是在方法文件中,而不是在类声明文件中进行的,因为类声明位于头文件中,程序可能将头文件包括在其他几个文件中。如果在头文件中进行初始化,将出现多个初始化语句副本,从而引发错误。
delete 定位new
delete空指针是合法的,没有副作用。
delete可与常规new运算符配合使用,但不能与定位new运算符配合使用。delete/[]知道从new/[]中分配了多少内存。
// 先通过new申请块内存
char* pch = new char[512];
// 通过定位new分配
Clas* pc1 = new(pch)Clas();
// 再分配
Clas* pc2 = new(pch+sizeof(Clas)) Clas();
// 显示调用析构函数
pc2->~Clas();
pc1->~Clas();
// 释放通过new[]分配的内存
delete[] pch;
嵌套结构和类
绝大部分情况下,private静态/非静态成员数据/函数只能内部访问
构造函数():初始化列表
{ 函数体,对象在执行这里的代码之前被创建 }
只有构造函数可以使用这种初始化列表语法
例如,对于const类成员,必须使用这种语法。对于被声明为引用的类成员,也必须使用这种语法。
TODO:拷贝构造做了什么
编译器默认生成的:
默认、拷贝、赋值、析构
如果将一个类对象复制或赋值给另一个类对象,逐成员赋值将使用成员类定义的复制构造函数和赋值运算符。
public继承建立 is-a 关系
水果是基类,香蕉是派生类
编译器生成的代码将在程序执行时,根据对象类型将虚函数名关联到 基类::fun或 派生类::fun中,总之编译器对虚方法使用动态联编。
空类对象的大小为1字节,含有虚函数的空类大小为虚表指针大小(32 bits/ 64 bits)
子类方法会将同名的父类方法隐藏
class Base
{
private:
/* data */
public:
virtual void base_fun1()
{
std::cout << "base fun1" << std::endl;
}
};
class Derive:public Base
{
private:
/* data */
public:
;
void base_fun1(int a){
;
}
};
int main()
{
map<int, int> mp = {{1,2},{3,5}};
Derive d;
d.Base::base_fun1();
}
抽象基类与protected
class BaseEllipse
{
protected:
double x_;
double y_;
public:
virtual void move(double nx, double ny) = 0;
virtual double area() const = 0;
BaseEllipse(double nx = 0, double ny=0):x_(nx), y_(ny) {}
virtual ~BaseEllipse() {}
};
class Ellipse : public BaseEllipse
{
protected:
double a_;
double b_;
double angle_;
public:
Ellipse(double nx=0, double ny=0, double a=0, double b=0, double angle=0):
BaseEllipse(nx,ny), a_(a), b_(b), angle_(angle) {}
virtual ~Ellipse() {}
virtual void move(double nx, double ny)
{
x_ = nx;
y_ = ny;
}
virtual double area() const
{
return 3.14159 * a_ * b_;
}
void rotate(double nang)
{
angle_ += nang;
}
void scale(double sx, double sy)
{
a_ *= sx;
b_ *= sy;
}
};
class Circle : public BaseEllipse
{
protected:
double r_;
public:
Circle(double x=0, double y=0, double r=1):
BaseEllipse(x, y), r_(r) {}
virtual ~Circle() {}
virtual void move(double nx, double ny)
{
x_ = nx;
y_ = ny;
}
virtual double area() const
{
return 3.14159 * r_ * r_;
}
void scale(double ss)
{
r_ *= ss;
}
};
void mpf(BaseEllipse* p)
{
std::cout << p->area();
}
int main()
{
Circle c;
mpf(&c);
}
子父类的构造、析构、拷贝构造与赋值运算符