提示:以下是本篇文章正文内容,下面案例可供参考
>.初始式是常量表达式的 const 对象称为 编译时常量 否则称为 运行时常量
>.编译时常量编译阶段就执行值替换了 值替换的过程类似宏展开
>.这和添加了static关键字的效果类似
>.编译器对实参到形参的转换规则
优先级 | 行为优先级 | 举例说明 |
---|---|---|
精确匹配 | 不做类型转换,直接匹配 | int 到 int |
细微转换 | 从数组名到数组指针; 从函数名到指向函数的指针; 从非 const 类型到 const 类型。 | |
类型提升后匹配 | 整型提升 | 从 bool、char、short 提升为 int; 从 char16_t、char32_t、wchar_t 提升为 int、long、long long。 |
小数提升 | 从 float 提升为 double。 | |
使用自动类型转换后匹配 | 整型转换 | 从 char 到 long; short 到 long; int 到 short; long 到 char。 |
小数转换 | 从 double 到 float。 | |
整数和小数转换 | 从 int 到 double,float; short 到 float; float 到 int; double 到 long。 | |
指针转换 | 从 int * 到 void *。 |
>.指调用重载函数时产生的二义性情况
class Test{
private:
int m_a;
int m_b;
public:
Test(int b): m_b(b), m_a(m_b){ }; //这会先执行 m_a(m_b) 从而把一个未知值赋值给 m_a
}
static 成员变量
static 成员函数
const 成员变量
const 成员函数
自身类型的静态成员
#include
using namespace std;
class B{
public:
const static B static_self;
double length {1.0};
double width {1.0};
double height {1.0};
B(double a,double b,double c):length{a},height{c} { };
B(double side):B(side,side,side) {}; //委托构造函数
static void show(){};
};
const B B::static_self(2.2); //必须在类外定义 类内声明
int main() {
cout<<B::static_self.height<<endl; //2.2
}
也可以将它定义成非const 类型的这样就可以修改了
#include
using namespace std;
class test{
public:
typedef int* pInt;
pInt show(pInt num);
};
test::pInt test::show(pInt num){
cout<<num<<endl;
return num;
}
int main(){
int count=5;
test obj;
obj.show(&count);
return 0;
}
>.一个类的成员变量如果是另一个类的对象,就称之为“成员对象”。包含成员对象的类叫封闭类(enclosed class)
>.封闭类必须要在其构造函数初始化列表指明其成员对象如何初始化(有默认构造能力的成员对象可以不用指明)
构造: 封闭类对象生成时,先执行所有成员对象的构造函数(顺序与其声明次序一致),然后才执行封闭类自己的构造函数。
析构: 先构造的后析构
>.派生类必须要在其构造函数初始化列表指明其成员对象和基类如何初始化(有默认构造能力的成员对象和基类构造函数可以不用指明)
>.继承与派生是一个概念,只不过站的角度不同
protected: 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected。
继承时的关键词限制
继承方式/基类成员 | public成员 | protected成员 | private成员 |
---|---|---|---|
public继承 | public | protected | 不可见 |
protected继承 | protected | protected | 不可见 |
private继承 | private | private | 不可见 |
名字遮蔽: 只要同名 基类成员就会被派生类成员遮蔽(包括成员变量和成员函数),派生类中如果要用基类的同名成员,需要用域解析符指定 (产生名字遮蔽的函数默认是不会构成重载的)
多继承菱形继承二义性: 如果多个基类中有同名的成员(可以是继承来的), 派生类中需要用域解析符指定用哪个基类的
内存模型:
假设从左到右对应地址从小到大
标红的即为虚基类
虚继承:
这时最终派生类使用虚基类的成员已无二义性
向上转型(将派生类赋值给基类):
虚函数和虚析构函数 只需要在声明时指定
>.原型一致指 函数名和参数列表和返回值类型都相同 但有一种例外: covariance of return type
class A{
public:
virtual A& f() {cout<<"A f"<<endl;A a;}
private:
};
class B:public A{
public:
B& f() { cout<<"B f"<<endl;B b;}
private:
};
#include
#include
using namespace std;
//基类
class Base
{
public:
Base()
{
str = new char[100];
cout << "Base constructor" << endl;
};
virtual ~Base()
{
delete[] str;
cout << "Base destructor" << endl;
};
protected:
char *str;
};
//二代
class Derived : public Base
{
public:
Derived()
{
name = new char[100];
cout << "Derived constructor" << endl;
};
~Derived()
{
delete[] name;
cout << "Derived destructor" << endl;
};
private:
char *name;
};
//三代
class Derived1 : public Derived
{
public:
Derived1()
{
name1 = new char[100];
cout << "Derived1 constructor" << endl;
};
~Derived1()
{
delete[] name1;
cout << "Derived1 destructor" << endl;
};
private:
char *name1;
};
int main()
{
SetConsoleOutputCP(65001);
// Base:初代
//Derived:二代
//Derived1:三代
cout << "---------初代指向二代----------" << endl;
Base *pb = new Derived();
delete pb;
cout << "---------二代指向二代----------" << endl;
Derived *pd = new Derived();
delete pd;
cout << "--------二代指向三代-----------" << endl;
Derived *pb1 = new Derived1();
delete pb1;
cout << "--------初代指向三代-----------" << endl;
Base *pb2 = new Derived1();
delete pb2;
cout << "-------------------" << endl;
}
内存泄漏时,程序会自动退出,可以把泄漏的语句注释掉
#include
#include
#include
#include
using namespace std;
void func()
{
cout<<"--------------------"<<endl;
cout << "OuterFunction" << endl;
}
//People类
class People
{
public:
People(char *name, int age);
~People();
protected:
void (*pf)();
char *m_name;
int m_age;
public:
virtual void display(); //用virtual 修饰类的成员函数或析构函数时 类的内存模型上会多一个指针 放在其首地址上
virtual void eating();
};
People::People(char *name, int age) : m_name(name), m_age(age) { pf = func; }
People::~People() { cout << "我终于被destructor了" << endl; }
void People::display()
{
cout << "Class People:" << this->m_name << "今年" << m_age << "岁了。" << endl;
}
void People::eating()
{
cout << "Class People:我正在吃饭,请不要跟我说话..." << endl;
}
//Student类
// class Student : public People
// {
// public:
// Student(string name, int age, float score);
// public:
// virtual void display();
// virtual void examing();
// protected:
// float m_score;
// };
// Student::Student(string name, int age, float score) : People(name, age), m_score(score) {}
// void Student::display()
// {
// cout << "Class Student:" << m_name << "今年" << m_age << "岁了,考了" << m_score << "分。" << endl;
// }
// void Student::examing()
// {
// cout << "Class Student:" << m_name << "正在考试,不要打扰啊" << endl;
// }
// //Senior类
// class Senior : public Student
// {
// public:
// Senior(string name, int age, float score, bool hasJob);
// public:
// virtual void display();
// virtual void partying();
// private:
// bool m_hasJob;
// };
// Senior::Senior(string name, int age, float score, bool hasJob) : Student(name, age, score), m_hasJob(hasJob) {}
// void Senior::display()
// {
// if (m_hasJob)
// {
// cout << "Class Senior:" << m_name << "以" << m_score << "的成绩从大学毕业了,并且顺利找到了工作,Ta今年" << m_age << "岁。" << endl;
// }
// else
// {
// cout << "Class Senior:" << m_name << "以" << m_score << "的成绩从大学毕业了,不过找工作不顺利,Ta今年" << m_age << "岁。" << endl;
// }
// }
// void Senior::partying()
// {
// cout << "Class Senior:快毕业了,大家都在吃散伙饭..." << endl;
// }
int main()
{
SetConsoleOutputCP(65001);
typedef void (*mypf)(); //函数指针 类型
typedef mypf** cxk; //指向一个(指向函数指针的指针)的 指针类型
typedef void (*lbw)(People *);
People *p = new People("赵红888", 29);
p->display();
// cout << sizeof(string) << endl; //string 32字节
cout << sizeof(People) << endl; //内存自动对齐 依8的倍数补齐 所以我前面放的是占8个字节的成员
//从这里可以看出 用virtual修饰类的成员函数或析构函数时 类的内存模型首地址上会多出个二维指针 cxk类型
cout << (char *)*((cxk)p + 2) << endl; //打印出 赵红888
((mypf) * ((cxk)p + 1))();//执行 OuterFunction
//这个虚函数用到了成员 所以需要传p进去
(*((lbw)**(cxk)p))(p); //成功访问 解引用得到虚函数表的地址 再解引用得到第一个元素(就是第一个函数指针)
(**(*(cxk)p + 1))(); //成功访问 解引用得到虚函数表的地址 滑动到第二个元素(第二个函数指针)
return 0;
}
后面的=0只是一个标识作用
int a = 5; int &b = a ;
本质就是给原来的"容器"起了一个alias,容器的地址是不变的(容器a和b)>.或者说是给原来的变量起了个小名,用这个小名和用原来的变量名是一样的 参数传递时和它初始化时同理
int &c = b;
意思是再起一个别名,abc引用的都是同一块数据#include
#include
#include
#include
using namespace std;
//People类
class People
{
public:
People(char *name, int age);
~People();
protected:
void (*pf)();
char *m_name;
int m_age;
public:
virtual void display(); //用virtual 修饰类的成员函数或析构函数时 类的内存模型上会多一个指针 放在其首地址上
};
People::People(char *name, int age) : m_name(name), m_age(age) { pf = func; }
People::~People() { cout << "People destructor了" << endl; }
void People::display()
{
cout << "Class People:" << this->m_name << "今年" << m_age << "岁了。" << endl;
}
int main()
{
SetConsoleOutputCP(65001);
typedef long long** cxk; //
People *p = new People("zhaoHong888", 29);
//解引用得到虚函数表的地址,-1 得到 type_info指针 的地址
const type_info **pTypeInfoAddr=(const type_info **)(*(cxk)p-1);
cout<<(*pTypeInfoAddr)->name()<<endl;
return 0;
}
输出 6People .返回值类型 operator运算符名称 (形参表列){
//TODO:
}
+ - * / % ^ & | ~ ! = < > += -= = /= %= ^= &= |= << >> <<= >>= == != <= >= && || ++ – , -> -> () [] new new[] delete delete[]
sizeof :? . ::
-> [] 函数调用运算符() =
#include
using namespace std;
//复数类
class Complex{
public:
Complex(): m_real(0.0), m_imag(0.0){ }
Complex(double real, double imag): m_real(real), m_imag(imag){ }
//转换构造函数 如果出现double类型 ,可能会通过调用此函数转换为Complex类型
Complex(double real): m_real(real), m_imag(0.0){ }
public:
friend Complex operator+(const Complex &c1, const Complex &c2);
public:
double real() const{ return m_real; }
double imag() const{ return m_imag; }
private:
double m_real; //实部
double m_imag; //虚部
};
//全局范围 重载+运算符
Complex operator+(const Complex &c1, const Complex &c2){
Complex c;
c.m_real = c1.m_real + c2.m_real;
c.m_imag = c1.m_imag + c2.m_imag;
return c;
}
int main(){
Complex c1(25, 35);
Complex c2 = c1 + 15.6; //等于 operator+(c1, Complex(15.6))
//如果是在类范围内重载+ 则下面这行会报错:no match for 'operator+' (operand types are 'double' and 'Complex')
Complex c3 = 28.23 + c1;//等于 operator+(Complex(28.23), c1)
cout<<c2.real()<<" + "<<c2.imag()<<"i"<<endl;
cout<<c3.real()<<" + "<<c3.imag()<<"i"<<endl;
return 0;
}
#include
using namespace std;
class Complex
{
double real, imag;
public:
//阻止了隐式转换的可能 (即double类型自动转换为Complex类型)
explicit Complex(double r = 0, double i = 0) : real(r), imag(i){};
//阻止了隐式转换的情况 但增加了Complex类型可以强制转换为double型的规则
explicit operator double() { return real; }
bool operator==(Complex rhs)
{
return (real == rhs.real && imag == rhs.imag) ? true : false;
}
};
int main()
{
//如果一个类有一个可以用单个参数调用的构造函数,那么这个构造函数就变成了转换构造函数
Complex c(1.2, 3.4);
cout << (double)c << endl; //输出 1.2 用explicit修饰后 需要明确的进行类型转换
// double f=c; //被 explicit 阻止 需要明确的对c进行类型转换
if (c == (Complex)5.5)//执行Complex(5.5) explicit阻止了隐式转换 所以这里用了强制类型转换
{
cout << "same" << endl;
}
else
{
cout << "No same" << endl;
};
}
函数模板:
函数声明和定义都要带上模板头
template
返回值类型 函数名(T &a, T &b){
//在函数体中可以使用类型参数 (上面的 T)
} //类型参数符号 可以自定义 只不过是个占位符而已 编译器根据实参推演出类型后会替换掉他们的位置
类模板:
//声明
templateclass Point{
//TODO:
};
//定义
template
void Point::setX(T1 x){
m_x = x;TODO:
}
显示专门化(Explicit Specialization)
#include
#include
#include
using namespace std;
typedef struct
{
string name;
int age;
float score;
} STU;
//函数模板========================声明=========================
template <class T>
const T &Max(const T &a, const T &b);
//函数模板的显示具体化(针对STU类型的显示具体化)
//写法一: 因为下方都指出来了所以<>里是空的
template <>
const STU &Max<STU>(const STU &a, const STU &b);
//写法二:
template <>
const STU &Max(const STU &a, const STU &b);
//经测试模板函数也能和普通函数构成重载
// const STU Max(const STU &a, const STU &b);
//重载<<
ostream &operator<<(ostream &out, const STU &stu);
int main()
{
SetConsoleOutputCP(65001);
int a = 10;
int b = 20;
cout << Max(a, b) << endl;
STU stu1 = {"王明", 16, 95.5};
STU stu2 = {"徐亮", 17, 98.7};
cout << Max(stu1, stu2) << endl;
return 0;
}
//函数模板============================定义===========================
template <class T>
const T &Max(const T &a, const T &b)
{
return a > b ? a : b;
}
//写法一 这次形参指明了数据类型
template <>
const STU &Max<STU>(const STU &a, const STU &b)
{
return a.score > b.score ? a : b;
}
//写法二
//template <>
//const STU &Max(const STU &a, const STU &b)
//{
// return a.score > b.score ? a : b;
//}
//经测试模板函数也能和普通函数构成重载
// const STU Max(const STU &a, const STU &b)
// {
// return a.score > b.score ? a : b;
// }
ostream &operator<<(ostream &out, const STU &stu)
{
out << stu.name << " , " << stu.age << " , " << stu.score;
return out;
}
/*回顾一下前面学习到的知识,在 C++ 中
,对于给定的函数名,可以有非模板函数、模板函数、显示具体化模板函数以及它们的重载版本,
在调用函数时,显示具体化优先于常规模板,而非模板函数优先于显示具体化和常规模板。*/
#include
using namespace std;
//类模板
template<class T1, class T2> class Point{
public:
Point(T1 x, T2 y): m_x(x), m_y(y){ }
public:
T1 getX() const{ return m_x; }
void setX(T1 x){ m_x = x; }
T2 getY() const{ return m_y; }
void setY(T2 y){ m_y = y; }
void display() const;
private:
T1 m_x;
T2 m_y;
};
template<class T1, class T2> //这里要带上模板头
void Point<T1, T2>::display() const{
cout<<"x="<<m_x<<", y="<<m_y<<endl;
}
//类模板的显示具体化(针对字符串类型的显示具体化)
template<> class Point<char*, char*>{
public:
Point(char *x, char *y): m_x(x), m_y(y){ }
public:
char *getX() const{ return m_x; }
void setX(char *x){ m_x = x; }
char *getY() const{ return m_y; }
void setY(char *y){ m_y = y; }
void display() const;
private:
char *m_x; //x坐标
char *m_y; //y坐标
};
//这里不能带模板头template<> 如果是部分显示具体化 则要带上模板头
void Point<char*, char*>::display() const{
cout<<"x="<<m_x<<" | y="<<m_y<<endl;
}
int main(){
( new Point<int, int>(10, 20) ) -> display();
( new Point<int, char*>(10, "东京180度") ) -> display();
( new Point<char*, char*>("东京180度", "北纬210度") ) -> display();
return 0;
}
#include
using namespace std;
//类模板
template<class T1, class T2> class Point{
public:
Point(T1 x, T2 y): m_x(x), m_y(y){ }
public:
T1 getX() const{ return m_x; }
void setX(T1 x){ m_x = x; }
T2 getY() const{ return m_y; }
void setY(T2 y){ m_y = y; }
void display() const;
private:
T1 m_x;
T2 m_y;
};
template<class T1, class T2> //这里需要带上模板头
void Point<T1, T2>::display() const{
cout<<"x="<<m_x<<", y="<<m_y<<endl;
}
//类模板的部分显示具体化
//通过这里的模板头和类名后的尖括号里的类型编译器可以知道哪个模板参数具体化了
template<typename T2> class Point<char*, T2>{
public:
Point(char *x, T2 y): m_x(x), m_y(y){ }
public:
char *getX() const{ return m_x; }
void setX(char *x){ m_x = x; }
T2 getY() const{ return m_y; }
void setY(T2 y){ m_y = y; }
void display() const;
private:
char *m_x; //x坐标
T2 m_y; //y坐标
};
template<typename T2> //这里需要带上模板头
void Point<char*, T2>::display() const{
cout<<"x="<<m_x<<" | y="<<m_y<<endl;
}
int main(){
( new Point<int, int>(10, 20) ) -> display();
( new Point<char*, int>("东京180度", 10) ) -> display();
( new Point<char*, char*>("东京180度", "北纬210度") ) -> display();
return 0;
}
非类型参数:
template void Swap(T (&a)[N], T (&b)[N]){}
通过传数组名能自动推断出数组元素的个数也就是N的值#include
using namespace std;
template <class Z,int* N>
Z test(Z val)
{
cout<<N<<endl;
return val;
}
int temp = 5;
int main()
{
//各个版本对非类型参数的要求略有不同 c++98要求必须将temp声明在全局,
//即使用static修饰也无济于事 直到c++17 (至少gcc下如此)
test<int,&temp>(97);
return 0;
}
模板特性:
>.在多文件编程中这可能会导致在链接期间找不到对应的实例;
显示实例化:(多文件编程)
>.显示实例化后 就可以把模板的声明和定义分开到不同的文件中了
template void Swap(double &a, double &b);
(Swap是一个函数模板,此针对double&类型显示实例化定义)声明写法: 在前面加 extern 即可
template class Point;
(Point是一个类模板)声明写法: 在前面加 extern 即可
类模板和模板类的概念
模板的继承与派生
template <class T1, class T2>
class A
{
Tl v1; T2 v2;
};
template <class T1, class T2>
class B : public A <T2, T1>
{
T1 v3; T2 v4;
};
template <class T>
class C : public B <T, T>
{
T v5;
};
int main()
{
B<int, double> obj1;
C<int> obj2;
return 0;
}
template<class T1, class T2>
class A{ T1 v1; T2 v2; };
template <class T>
class B: public A <int, double>{T v;};
int main() { B <char> obj1; return 0; }
模板参数的作用域:
#include
#include
using namespace std;
template <class T1, class T2>
class Pair
{
private:
T1 key; //关键字
T2 value; //值
public:
Pair(T1 k, T2 v) : key(k), value(v) { };
bool operator < (const Pair<T1, T2> & p) const;
//如果用T1 T2 那将报错,declaration of template parameter 'T1' shadows template parameter
template <class T3, class T4>//函数模板作友元
friend ostream & operator << (ostream & o, const Pair<T3, T4> & p);
};
template <class T1, class T2>
bool Pair <T1, T2>::operator< (const Pair<T1, T2> & p) const
{ //“小”的意思就是关键字小
return key < p.key;
}
template <class T1, class T2>//这就是模板的作用域
ostream & operator << (ostream & o, const Pair<T1, T2> & p)
{
o << "(" << p.key << "," << p.value << ")";
return o;
}
int main()
{
Pair<string, int> student("Tom", 29);
Pair<int, double> obj(12, 3.14);
cout << student << " " << obj;
return 0;
}
类模板与友元
#include
using namespace std;
template<class T>
class A
{
public:
void Func(const T & p)
{
cout << p.v;
}
};
template <class T>
class B
{
private:
T v;
public:
B(T n) : v(n) { }
template <class T2>
friend class A; //把类模板A声明为友元
};
int main()
{
B<int> b(5);
A< B<int> > a; //用B替换A模板中的 T
a.Func(b);
return 0;
}
类模板中的静态成员
#include
using namespace std;
template <class T>
class A
{
private:
static int count;
public:
A() { count ++; }
~A() { count -- ; };
A(A &) { count ++ ; }
static void PrintCount() { cout << count << endl; }
};
template<> int A<int>::count = 0;
template<> int A<double>::count = 0; //不同的模板类中的静态成员是不同的内存
int main()
{
A<int> ia;
A<double> da;
// A f; error: undefined reference to `A::count'
ia.PrintCount();
da.PrintCount();
return 0;
}
一旦出现异常程序会自动退出,而使用了异常处理,可以对这种情况进行补救
#include // 好像不加也没什么
#include
#include
#include
using namespace std;
class Base
{
};
class Derived : public Base
{
};
int main()
{
//catch 在匹配异常类型的过程中,也会进行类型转换,但是这种转换受到了更多的限制,仅能进行「向上转型」、「const 转换」和「数组或函数指针转换」,其他的都不能应用于 catch。
//注意指针不能自动由const*转换到非const指针
try {
// 发生异常后,程序的执行流会沿着函数的调用链往前回退,直到遇见 try 才停止。
// 所以检测到异常后 其后面的代码都不会被执行
char *p = NULL;
throw 5; //当抛出异常后 捕捉不到 则交给系统处理终止程序
// throw(string) "exception";
// throw(const) 5;
// throw "Unknown Exception";
throw Derived(); //抛出自己的异常类型,实际上是创建一个Derived类型的匿名对象
cout << "This statement will not be executed." << endl;
}
catch (exception &a)
{ //C++ 语言本身以及标准库中的函数抛出的异常,都是 exception 类或其子类的异常。也就是说,抛出异常时,会创建一个 exception 类或其子类的对象。
//C++语言本身或者标准库抛出的异常都是 exception 的子类,称为标准异常(Standard Exception)你可以不throw 捕获所有的标准异常
//exception类具体来说就是标准库对一些类东西做了封装 比如当对某个自定义动态数组做越界取值 就throw exception 并根具具体情况传入构造参数
cout << "Exception type: int" << endl;
}
catch (int e) //如果不需要用到 也可以不写参数名
{
cout << "Exception typechar int :" <<e<< endl;
}
catch (char const *e)
{
cout << "Exception typechar char const* :" <<e<< endl;
}
catch (string &e)
{
cout << "Exception typechar string" <<e<< endl;
}
catch (Base)
{ //匹配成功(向上转型)
cout << "Exception type: Base" << endl;
}
catch (Derived)
{
cout << "Exception type: Derived" << endl;
}
//================================标准库抛出的异常======================
string str = "http://c.biancheng.net";
int a[9] = {0};
int b = 5;
try
{
char ch2 = str.at(100);
cout << ch2 << endl;
}
catch (exception &e)
{ //exception类位于头文件中
cerr << e.what() << endl;
cout << e.what() << endl;
}
return 0;
}
Exception specification
double func (char param) throw (int, char, exception);
double func (char param) throw ();
-fno-elide-constructors
参数 阻止对此的优化 #include
#include
using namespace std;
class Student{
public:
Student(string name = "", int age = 0, float score = 0.0f); //普通构造函数
Student(const Student &stu); //拷贝构造函数
public:
Student & operator=(const Student &stu); //重载=运算符
void display();
private:
string m_name;
int m_age;
float m_score;
};
Student::Student(string name, int age, float score): m_name(name), m_age(age), m_score(score){
cout<<"Default constructor was called."<<endl;
}
//拷贝构造函数
Student::Student(const Student &stu){
this->m_name = stu.m_name;
this->m_age = stu.m_age;
this->m_score = stu.m_score;
cout<<"Copy constructor was called."<<endl;
}
//重载=运算符
Student & Student::operator=(const Student &stu){
this->m_name = stu.m_name;
this->m_age = stu.m_age;
this->m_score = stu.m_score;
cout<<"operator=() was called."<<endl;
return *this;
}
void Student::display(){
cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
Student func(Student stud){
cout<<"+++++++++++++++++++++++++++++++"<<endl;
Student s("小明", 16, 90.5); //调用默认
cout<<"------------------------------"<<endl;
return s;//不调用
}
int main(){
//里面Student()调用一次默认 赋值给stu时不调用 相当于把函数返回的当成stu
Student stu = func(Student());
cout<<"=========================="<<endl;
//stu1、stu2、stu3都会调用普通构造函数Student(string name, int age, float score)
Student stu1("小明", 16, 90.5);
Student stu2("王城", 17, 89.0);
Student stu3("陈晗", 18, 98.0);
//初始化中的赋值操作不会调用赋值运算符
Student stu4 = stu1; //调用拷贝构造函数Student(const Student &stu)
stu4 = stu2; //调用operator=()
stu4 = stu3; //调用operator=()
Student stu5; //调用普通构造函数Student()
stu5 = stu1; //调用operator=()
stu5 = stu2; //调用operator=()
return 0;
}
关键字 | 说明 |
---|---|
static_cast | 用于良性转换,一般不会导致意外发生,风险很低。 |
const_cast | 用于 const 与非 const、volatile 与非 volatile 之间的转换。 |
reinterpret_cast | 高度危险的转换,这种转换仅仅是对二进制位的重新解释,不会借助已有的转换规则对数据进行调整,但是可以实现最灵活的 C++ 类型转换。 |
dynamic_cast | 借助 RTTI,用于类型安全的向下转型(Downcasting)。 |
#include
#include
using namespace std;
#define OUTSCREEN(msg, ...) printf(msg, __VA_ARGS__)
void c(int a)
{
cout << "我是普通的函数" << endl;
}
class Complex
{
double real, imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i){};
double operator()(const char *str) //函数对象
{
cout << real << str << imag << endl;
return real + imag;
}
operator double() { return real; } //重载强制类型转换运算为double型
};
int main()
{
SetConsoleOutputCP(65001);
Complex c(1.2, 3.4);
cout << c << endl; //输出 1.2
double n = 2 + c; //等价于 double n = 2 + c. operator double()
cout << n << endl; //输出 3.2
c("八嘎呀路"); //函数对象的调用优先于普通函数且不产生重载 一个
// Complex z("世界"); //不能通过实例化调用 no matching function for call to 'Complex::Complex
// void (*fun)(int) = c; //一个同名对象的产生 那个对应的同名函数将被编译器忽略作废
// fun(5);
Complex *obj;
cout << (*obj)("指针是怎么调用函数对象的函数的?") << endl;
}
>.在c++中不允许直接传递函数,虽然可以通过函数指针或引用的形式来传递,但与函数对象相比会产生间接调用的开销
#include
#include
using namespace std;
namespace Li{ //小李的变量定义
int a = 1 ;
int b = 1;
}
namespace Han{ //小韩的变量定义
double a = 3.4;
double b = 3.4;
}
int main(void)
{
//-------------------------普通用法------------------------------------
Li::a = 2; //使用小李定义的变量a
Han::a = 1.2; //使用小韩定义的变量 a
//-------------------------单个变量领域展开------------------------------------
//向下覆盖 领域展开 能覆盖掉无视掉 using namespace Han 且同一作用域内对此同名变量领域展开只能有一个人
using Li::a; // 默认后面所有的a都是指Li里面的a 除非用域解析符::指定
a = 3 ; //使用小李定义的变量 a
using Han::a // error 且同一作用域内对此同名变量领域展开只能有一个人
using Han::b // ok
Han::a = 4.4; //使用小韩定义的变量 a
//-------------------------namespace领域展开------------------------------------
//同一作用域内只能有一个人namespace领域展开
using namespace Li; //向下覆盖 直到单个变量领域展开出现 或使用域解析符指定,否则都是用的Li里面的变量
a = 5; //使用小李定义的变量 a
using Han::a ; //Han单个变量领域展开
a = 6.3 ; //使用小韩定义的变量 a
cout<<Li::a<<' '<<Han::a<<endl;
return 0;
}
扩展名称空间
namespace Li{ //小李的变量定义
int a = 1 ;
int b = 1;
}
namespace Han{ //小韩的变量定义
double a=3.4;
double b=3.4;
}
namespace Li{ //小李名称空间的继续, 被称为扩展名称空间的定义
char c= '0';
}
嵌套
namespace outer
{
double max(const std::vector<double>& data)
{
//body code....
}
double min(const std::vector<double>& data)
{
//body code...
}
namespace inner
{
void normalize(std::vector<double>& data)
{
//...
double minValue { min(data) };//Calls min() in outer namespace
//...
}
}
}
>.只需要在定义处使用inline关键字
inline void swap1(int *a, int *b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
>.如果函数体内代码复杂庞大,你还用inline修饰此函数 编译器大概是不鸟你的
static
关键字),并且头文件被多次#include
后也不会引发重复定义错误。#include
#include
//可变参数的函数
void vair_fun(int count, ...)
{
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i)
{
int arg = va_arg(args, int);
std::cout << arg << " ";
}
va_end(args);
}
int main()
{
//可变参数有 4 个,分别为 10、20、30、40
vair_fun(4, 10, 20, 30,40);
return 0;
}
当可变参数中包含 char 类型的参数时,va_arg 宏要以 int 类型的方式读取;当可变参数中包含 short 类型的参数时,va_arg 宏要以 double 类型的方式读取。
Better late than never