为什么以下工作?
void foo() {
cout << "Foo to you too!\n";
};
int main() {
void (*p1_foo)() = foo;
void (*p2_foo)() = *foo;
void (*p3_foo)() = &foo;
void (*p4_foo)() = *&foo;
void (*p5_foo)() = &*foo;
void (*p6_foo)() = **foo;
void (*p7_foo)() = **********************foo;
(*p1_foo)();
(*p2_foo)();
(*p3_foo)();
(*p4_foo)();
(*p5_foo)();
(*p6_foo)();
(*p7_foo)();
}
Run Code Online (Sandbox Code Playgroud) 所以这是我所在的框.我想了解为什么在你的接口类中有一个"虚拟析构函数"很重要.如果你可以挂到最后,你会看到为什么那些东西在引号中...我也想让所有的词汇绝对正确.到目前为止,这是我处理过程的地方:
有时您有基类,有时您有从基类继承的派生类.
如果你有一个基指针,它发现自己指向一个派生对象,你还希望从该指针指向一个派生对象的成员函数调用,就像它实际上被调用一样从派生对象中,您调用的成员函数最好在基类中声明为virtual.
接口是任何只有纯虚函数的类.如果从此接口类派生新类并实现所有纯虚函数,则最终可以创建派生类的实例.
你永远不可能有一个接口类的实例,但你可以有一个指向接口类的实例.
如果你有一个实际指向派生类对象的指针接口类(实际上,我想如果#4是正确的话,它总是必须这样),如果你决定通过它删除那个对象你的指针,如果你的接口类中没有"虚拟析构函数",你破坏派生对象的意图只会作为一个调用来执行,以破坏基础对象(即接口类),因为没有虚拟析构函数,事物永远不会达到实际调用派生对象的析构函数的程度 - 从而导致内存泄漏.
唷.好的,如果这听起来是正确的,请回答我的问题.仅仅在你的界面中声明一个虚拟析构函数就足够了:
virtual ~iFace();
Run Code Online (Sandbox Code Playgroud)
这对我来说是错误的......所以如果你像这样使析构函数变为虚拟,会发生什么:
virtual ~iFace() = 0;
Run Code Online (Sandbox Code Playgroud)
既然它们只是声明,那么它们中的任何一个都算是"接口类中的虚拟析构函数"吗?你甚至可以有一个声明但未定义的析构函数吗?只有它是纯粹的虚拟我猜...
无论如何,所以回到标题问题......我真的要尽可能快......这是拍摄的钱......如果你的"界面类中的虚拟析构函数"需要至少一个像这样的空定义:
virtual ~iFace() {};
Run Code Online (Sandbox Code Playgroud)
那个成员函数不是纯虚拟的(不能因为你给了它一个定义),因此你的类不再是一个接口(它不仅包含纯虚拟成员函数).
这意味着如果为接口定义虚拟析构函数,则不再具有接口(而只是一些抽象基类).这只是滥用语言吗?我明白发生了什么吗?
注意:这一切都来自于问自己"什么是界面?" 然后阅读这个问题的答案: 你如何在C++中声明一个接口?
希望漫长的骑行时间不会太长,但我决心完全理解这些概念及其相关的词汇.
我似乎记得,在过去的时候,>在处理嵌套模板参数时,要警告不要将两个字符紧挨着(没有空格).我甚至模糊地记得声明向量的向量,并遇到这个编译错误.
但现在我发现编译可怕的东西绝对没有错>>...
我的问题是这样的:
该公约在什么时候成为可接受的做法?
它是标准C++的一部分吗?
它是否始终是标准的一部分,我在大学里使用的编译器(以及我所拥有的教授)只是不支持它?
也许这些问题有点历史,但对我而言,正确的历史背景似乎使得实际的记忆变得微不足道.
因此,为了更好地理解新/删除(真的用小例子向我自己证明为什么接口需要虚拟析构函数),我想了解内存泄漏,以便我可能生活在对它们的恐惧中.但是我很难接触到泄漏,可以这么说; 实际上,我也很难用new/delete.
这是我最简单的版本:
int* P1 = new int(43);
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;
delete P1;
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;
Run Code Online (Sandbox Code Playgroud)
这打印:
P1 = 0xcc0340
*P1 = 43
P1 = 0xcc0340
*P1 = 43
Run Code Online (Sandbox Code Playgroud)
我在课堂上有一些更复杂的东西,但是这个例子说明了我的失败.我认为删除需要一个指针并释放它的内存,从而使指针无效或至少它指向什么?我必须做一些非常简单的事情.
注意:我正在使用g ++编译器(我听说这是非常好的,并且应该非常接近标准).
假设您已经声明了一组int:
int a[3] = { 4, 5, 6 };
Run Code Online (Sandbox Code Playgroud)
现在让我们说你真的想要声明对该数组的引用(除了Bjarne说语言支持它之外,没关系为什么).
案例1 - 如果您尝试:
int*& ra = a;
Run Code Online (Sandbox Code Playgroud)
那么编译器就会说:
"invalid initialization of non-const reference of type `int*&' from a temporary of type `int*'"
Run Code Online (Sandbox Code Playgroud)
首先,为什么'a'是一个临时变量(即它在内存中没有位置?)......
无论如何,没问题,每当我看到一个非常量错误时,我会尝试抛出一个const ...
案例2 - 如果您尝试:
int*const&rca = a; //wish I knew where the spaces should go (but my other post asking about this sort of protocol got a negative rank while many of the answers got ranked highly -- aha! there are stupid questions!)
Run Code Online (Sandbox Code Playgroud)
然后一切都很酷,它编译,你得到一个数组的引用. …
那么让我们说我有一个数组:
int a[3] = { 1, 2, 3 };
Run Code Online (Sandbox Code Playgroud)
现在,如果我要检查'a'的类型,我会得到:
cout<<typeid(a).name(); // prints 'A3_i'
Run Code Online (Sandbox Code Playgroud)
现在如果我取'a'的地址,然后取消引用该地址,类型不会改变(我真的很喜欢,因为在我看来'取地址'和'取消引用'是反向操作):
cout<<typeid(*&a).name(); // also prints 'A3_i'
Run Code Online (Sandbox Code Playgroud)
然而,如果我首先取消引用'a',然后取用它的地址,类型确实会改变(我承认我很难不喜欢,因为当我取消引用数组时我应该得到一个int,当我拿到地址时那个int,我应该得到一个指向int的指针,事实证明我做了):
cout<<typeid(&*a).name(); // prints 'Pi'
Run Code Online (Sandbox Code Playgroud)
所以这是我的两个问题:
1)一旦数组类型已经衰减成指针类型,无论如何都要将它恢复为数组类型?
我尝试了一种明显的铸造策略 - 就像你不关心一样:
cout<<typeid( (int[3]) &*a).name(); // does not compile
// "error: ISO C++ forbids casting to an array type `int [3]'"
Run Code Online (Sandbox Code Playgroud)
还有另一个演员可以奏效吗?或者这种类型的转换严格禁止?
2)你是否能够回到数组类型,究竟是什么信息在衰变到指针过程中被切片和丢失?
我知道指针类型和数组类型不等价.我假设array-type是存储在指针类型中的信息的严格超集.这听起来不错吗?
我在其他问题中已经读过,数组类型中的额外信息是:知道数组是否在堆栈上,以及它的大小(它必须以某种方式知道数组的大小,因为它是类型,对吗?).数组类型中是否隐藏了其他任何信息?
所以我正在玩unordered_mapSTL 的新标准化.我的代码有点像这样,我只是创建一个unordered_map,填充并打印出来:
unordered_map<int,string> m1;
m1[5]="lamb";
m1[2]="had";
m1[3]="a";
m1[1]="mary";
m1[4]="little";
m1[7]="fleece";
m1[6]="whose";
m1[10]="fleecey";
m1[8]="was";
m1[9]="all";
for(unordered_map<int,string>::const_iterator i = m1.begin(); i != m1.end(); ++i)
cout<<i->first<<" "<<i->second<<endl;
Run Code Online (Sandbox Code Playgroud)
但是,我获得的输出因此被订购:
1 mary
2 had
3 a
4 little
5 lamb
6 whose
7 fleece
8 was
9 all
10 fleecey
Run Code Online (Sandbox Code Playgroud)
但是我不想为我的地图订购付出代价!这就是我使用unordered_map的原因......这里发生了什么?
附加说明:我正在使用gcc version 4.3.4 20090804 (release) 1 (GCC)并正在编译这样的g++ -std=c++0X maptest.cpp
我看了,找不到这个问题的答案,所以这里就是这样.
我有一些数据(1 X 1000矢量称为数据),我想绘制直方图信息.如果我使用该histogram(data)命令,那么我得到一个足够好的直方图,其中x轴被均匀地划分为十个桶(数据的最大值和最小值之间的十个相等间隔的中点值),并且y轴记录如何每个桶都发生了很多事情.
我真正想要的是相同的情节,只是y轴代表铲斗间隔,x轴代表每个铲斗的数量......
这样我就可以将它粘贴到一些其他信息旁边的子图中,一切都会更容易理解(看起来非常酷).有什么简单的方法可以实现这一目标?谢谢!
注意:我正在使用g ++编译器(我听说这是非常好的,并且应该非常接近标准).
我有一个我能想到的最简单的课程:
class BaseClass {
public:
int pub;
};
Run Code Online (Sandbox Code Playgroud)
然后我有三个同样简单的程序来创建BaseClass对象并打印出他们数据的[未初始化]值.
BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
Run Code Online (Sandbox Code Playgroud)
打印出:
B1.pub = 1629556548
Run Code Online (Sandbox Code Playgroud)
哪个好.我实际上认为它会被初始化为零,因为它是POD或Plain Old Datatype或类似的东西,但我想不是吗?到现在为止还挺好.
BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;
Run Code Online (Sandbox Code Playgroud)
打印出:
B1.pub = 1629556548
B2.pub = 0
Run Code Online (Sandbox Code Playgroud)
这绝对是奇怪的.我以相同的方式创建了两个相同的对象.一个被初始化而另一个没有.
BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;
BaseClass* pB3 = new BaseClass;
cout<<"B3.pub = "<<pB3->pub<<endl;
Run Code Online (Sandbox Code Playgroud)
打印出:
B1.pub = 0
B2.pub = 0
B3.pub = 0
Run Code Online (Sandbox Code Playgroud)
这是最奇怪的.它们都被初始化为零.我所做的只是添加两行代码,它改变了以前的行为.
那么这只是"未初始化的数据导致未指明的行为"的情况,还是"引擎盖下"更合乎逻辑?
我真的想了解默认的构造函数/析构函数行为,因为我觉得完全理解继承的东西非常重要.
涉嫌采访的问答在这里.
以下代码是否会编译(在C中)?
#define X 8;
int main(void)
{
++X; // will this line compile?
}`
Run Code Online (Sandbox Code Playgroud)
我不是C的专家,但我知道一些C++并认为:当然不是,你不能增加数字8,它是一个右值.当然,预处理程序替换X与8尝试编译之前,并且当它尝试编译,它就会失败正是由于这个原因.然后,我是一个阅读面试问题的网站,所以我想谁知道...
这是给出的解释:
"严格来说,前缀(或后缀)增量运算符的操作数必须是一个不可修改的左值.现在我们知道左值是什么,我们必须问自己X是否是左值.X是一个宏,这意味着它不能识别内存中的位置 - 宏通过预处理器使用简单的文本替换.因为宏不存在于内存区域,所以它们不是左值.这意味着X不能用作前缀增量的操作数因此,上面显示的代码将无法编译."
这个解释是否像我认为的那样是无聊的?
你上面能找到多少错误?我想也许这应该是面试问题......
这很有趣:
"直观地说,你可能会说上面的代码无法编译 - 如果不确切知道原因.但是,在面试的情况下,你会被期望提供一些像上面给出的那样的推理.简单的是或否答案刚赢了"在接受采访时削减它." (!)